obsidian-anchor 0.2.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 +21 -0
- package/README.md +209 -0
- package/dist/chunk/chunker.js +169 -0
- package/dist/chunk/chunker.js.map +1 -0
- package/dist/chunk/markdown.js +49 -0
- package/dist/chunk/markdown.js.map +1 -0
- package/dist/chunk/types.js +5 -0
- package/dist/chunk/types.js.map +1 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/container.js +84 -0
- package/dist/container.js.map +1 -0
- package/dist/edit/safeEdit.js +130 -0
- package/dist/edit/safeEdit.js.map +1 -0
- package/dist/edit/snapshots.js +31 -0
- package/dist/edit/snapshots.js.map +1 -0
- package/dist/embeddings/local.js +61 -0
- package/dist/embeddings/local.js.map +1 -0
- package/dist/embeddings/openai.js +63 -0
- package/dist/embeddings/openai.js.map +1 -0
- package/dist/embeddings/provider.js +5 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/index/indexer.js +108 -0
- package/dist/index/indexer.js.map +1 -0
- package/dist/index/walk.js +28 -0
- package/dist/index/walk.js.map +1 -0
- package/dist/index/watcher.js +115 -0
- package/dist/index/watcher.js.map +1 -0
- package/dist/index.js +192 -0
- package/dist/index.js.map +1 -0
- package/dist/server.js +61 -0
- package/dist/server.js.map +1 -0
- package/dist/store/chunkStore.js +82 -0
- package/dist/store/chunkStore.js.map +1 -0
- package/dist/store/db.js +59 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/schema.js +67 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/search.js +33 -0
- package/dist/store/search.js.map +1 -0
- package/dist/store/types.js +3 -0
- package/dist/store/types.js.map +1 -0
- package/dist/store/vectorStore.js +77 -0
- package/dist/store/vectorStore.js.map +1 -0
- package/dist/tools/cite.js +51 -0
- package/dist/tools/cite.js.map +1 -0
- package/dist/tools/restoreNote.js +49 -0
- package/dist/tools/restoreNote.js.map +1 -0
- package/dist/tools/safeEdit.js +65 -0
- package/dist/tools/safeEdit.js.map +1 -0
- package/dist/tools/searchNotes.js +47 -0
- package/dist/tools/searchNotes.js.map +1 -0
- package/dist/tools/verifyGrounding.js +66 -0
- package/dist/tools/verifyGrounding.js.map +1 -0
- package/dist/util/hash.js +6 -0
- package/dist/util/hash.js.map +1 -0
- package/dist/util/logger.js +41 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/timeout.js +19 -0
- package/dist/util/timeout.js.map +1 -0
- package/dist/verify/anthropic.js +126 -0
- package/dist/verify/anthropic.js.map +1 -0
- package/dist/verify/decompose.js +100 -0
- package/dist/verify/decompose.js.map +1 -0
- package/dist/verify/localVerifier.js +89 -0
- package/dist/verify/localVerifier.js.map +1 -0
- package/dist/verify/pipeline.js +165 -0
- package/dist/verify/pipeline.js.map +1 -0
- package/dist/verify/score.js +64 -0
- package/dist/verify/score.js.map +1 -0
- package/dist/verify/types.js +3 -0
- package/dist/verify/types.js.map +1 -0
- package/dist/verify/verifier.js +2 -0
- package/dist/verify/verifier.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { AutoModelForSequenceClassification, AutoTokenizer, env } from "@huggingface/transformers";
|
|
2
|
+
import { logger } from "../util/logger.js";
|
|
3
|
+
// Default is English. For non-English vaults, point this at a multilingual NLI
|
|
4
|
+
// ONNX model via ANCHOR_NLI_MODEL (label order is auto-detected from id2label).
|
|
5
|
+
const MODEL_ID = process.env.ANCHOR_NLI_MODEL ?? "Xenova/nli-deberta-v3-small";
|
|
6
|
+
/**
|
|
7
|
+
* Local, no-API-key grounding verifier built on a cross-encoder NLI model. For
|
|
8
|
+
* each (passage, claim) pair it reads the full entailment / contradiction /
|
|
9
|
+
* neutral distribution — crucially, this distinguishes "contradicted" from
|
|
10
|
+
* merely "unsupported", which similarity search alone cannot do.
|
|
11
|
+
*/
|
|
12
|
+
export class LocalVerifier {
|
|
13
|
+
id = "local-nli-deberta";
|
|
14
|
+
loaded = null;
|
|
15
|
+
constructor(cacheDir) {
|
|
16
|
+
if (cacheDir) {
|
|
17
|
+
env.cacheDir = cacheDir;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
load() {
|
|
21
|
+
if (this.loaded === null) {
|
|
22
|
+
logger.info("Loading local NLI verifier (first run downloads the model)", { model: MODEL_ID });
|
|
23
|
+
this.loaded = (async () => {
|
|
24
|
+
try {
|
|
25
|
+
const tokenizer = (await AutoTokenizer.from_pretrained(MODEL_ID));
|
|
26
|
+
const model = (await AutoModelForSequenceClassification.from_pretrained(MODEL_ID));
|
|
27
|
+
return { tokenizer, model, labelIndex: resolveLabelIndex(model.config.id2label) };
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
this.loaded = null; // allow a retry on the next call
|
|
31
|
+
throw new Error(`Failed to load the local NLI verifier '${MODEL_ID}'. It downloads on first use — check network access and disk space.`, { cause: error });
|
|
32
|
+
}
|
|
33
|
+
})();
|
|
34
|
+
}
|
|
35
|
+
return this.loaded;
|
|
36
|
+
}
|
|
37
|
+
async entail(input) {
|
|
38
|
+
if (input.passages.length === 0)
|
|
39
|
+
return [];
|
|
40
|
+
const { tokenizer, model, labelIndex } = await this.load();
|
|
41
|
+
// One (passage, claim) pair at a time. Batching pairs with padding produced
|
|
42
|
+
// misaligned/incorrect labels in transformers.js (unrelated passages scored
|
|
43
|
+
// as high-confidence contradictions), so we trade a little speed for
|
|
44
|
+
// correctness — premise = the note passage, hypothesis = the claim.
|
|
45
|
+
const judgements = [];
|
|
46
|
+
for (const passage of input.passages) {
|
|
47
|
+
const inputs = await tokenizer(passage.text, { text_pair: input.claim, truncation: true });
|
|
48
|
+
const output = await model(inputs);
|
|
49
|
+
judgements.push(judge(passage.index, softmax(Array.from(output.logits.data)), labelIndex));
|
|
50
|
+
}
|
|
51
|
+
return judgements;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Maps the model's id2label to our Verdict indices, tolerant of label naming. */
|
|
55
|
+
function resolveLabelIndex(id2label) {
|
|
56
|
+
const index = {};
|
|
57
|
+
for (const [idx, label] of Object.entries(id2label)) {
|
|
58
|
+
const lower = label.toLowerCase();
|
|
59
|
+
const i = Number(idx);
|
|
60
|
+
if (lower.includes("entail"))
|
|
61
|
+
index.supported = i;
|
|
62
|
+
else if (lower.includes("contradic"))
|
|
63
|
+
index.contradicted = i;
|
|
64
|
+
else if (lower.includes("neutral"))
|
|
65
|
+
index.neutral = i;
|
|
66
|
+
}
|
|
67
|
+
if (index.supported === undefined ||
|
|
68
|
+
index.contradicted === undefined ||
|
|
69
|
+
index.neutral === undefined) {
|
|
70
|
+
throw new Error(`Unexpected NLI label set: ${JSON.stringify(id2label)}`);
|
|
71
|
+
}
|
|
72
|
+
return { supported: index.supported, contradicted: index.contradicted, neutral: index.neutral };
|
|
73
|
+
}
|
|
74
|
+
function judge(passageIndex, probs, labelIndex) {
|
|
75
|
+
const scored = [
|
|
76
|
+
{ label: "supported", confidence: probs[labelIndex.supported] ?? 0 },
|
|
77
|
+
{ label: "contradicted", confidence: probs[labelIndex.contradicted] ?? 0 },
|
|
78
|
+
{ label: "neutral", confidence: probs[labelIndex.neutral] ?? 0 },
|
|
79
|
+
];
|
|
80
|
+
const best = scored.reduce((a, b) => (b.confidence > a.confidence ? b : a));
|
|
81
|
+
return { passageIndex, label: best.label, confidence: best.confidence };
|
|
82
|
+
}
|
|
83
|
+
function softmax(values) {
|
|
84
|
+
const max = Math.max(...values);
|
|
85
|
+
const exps = values.map((value) => Math.exp(value - max));
|
|
86
|
+
const sum = exps.reduce((a, b) => a + b, 0);
|
|
87
|
+
return exps.map((exp) => exp / sum);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=localVerifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localVerifier.js","sourceRoot":"","sources":["../../src/verify/localVerifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { logger } from "../util/logger.js";
|
|
2
|
+
import { withTimeout } from "../util/timeout.js";
|
|
3
|
+
import { decompose } from "./decompose.js";
|
|
4
|
+
import { aggregate, claimScore, DEFAULT_THRESHOLDS } from "./score.js";
|
|
5
|
+
const DEFAULT_DECISIVE_MIN_CONFIDENCE = 0.5;
|
|
6
|
+
const DEFAULT_EVIDENCE_MIN_SCORE = 0.35;
|
|
7
|
+
const DEFAULT_VERIFY_TIMEOUT_MS = 30_000;
|
|
8
|
+
/**
|
|
9
|
+
* The heart of Anchor: decompose an answer into claims, retrieve candidate
|
|
10
|
+
* evidence for each (reusing search), have the verifier judge entailment, then
|
|
11
|
+
* score and aggregate into a grounded / flagged / refused verdict.
|
|
12
|
+
*/
|
|
13
|
+
export class GroundingPipeline {
|
|
14
|
+
search;
|
|
15
|
+
verifier;
|
|
16
|
+
options;
|
|
17
|
+
constructor(search, verifier, options) {
|
|
18
|
+
this.search = search;
|
|
19
|
+
this.verifier = verifier;
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
async verify(answer, providedSources) {
|
|
23
|
+
const claims = decompose(answer);
|
|
24
|
+
const perClaim = [];
|
|
25
|
+
for (const claim of claims) {
|
|
26
|
+
const candidates = providedSources ?? (await this.search.search(claim.text, this.options.knn));
|
|
27
|
+
perClaim.push(await this.judgeClaim(claim.text, candidates));
|
|
28
|
+
}
|
|
29
|
+
return aggregate(perClaim, this.options.thresholds ?? DEFAULT_THRESHOLDS);
|
|
30
|
+
}
|
|
31
|
+
async judgeClaim(claimText, candidates) {
|
|
32
|
+
// Only judge passages that are actually relevant to the claim. The NLI model
|
|
33
|
+
// is unreliable on off-topic text (it can emit spurious high-confidence
|
|
34
|
+
// contradictions), so a low-relevance passage must not be able to refute — or
|
|
35
|
+
// support — a claim.
|
|
36
|
+
const minScore = this.options.evidenceMinScore ?? DEFAULT_EVIDENCE_MIN_SCORE;
|
|
37
|
+
const relevant = candidates.filter((chunk) => chunk.score >= minScore);
|
|
38
|
+
if (relevant.length === 0) {
|
|
39
|
+
return { claim: claimText, verdict: "neutral", score: 0, evidence: [] };
|
|
40
|
+
}
|
|
41
|
+
const passages = relevant.map((chunk, index) => ({
|
|
42
|
+
index,
|
|
43
|
+
path: chunk.notePath,
|
|
44
|
+
anchor: chunk.anchor,
|
|
45
|
+
text: cleanText(chunk.text),
|
|
46
|
+
}));
|
|
47
|
+
let judgements;
|
|
48
|
+
try {
|
|
49
|
+
judgements = await withTimeout(this.verifier.entail({ claim: claimText, passages }), this.options.verifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS, "verifier");
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.warn("Verifier failed or timed out; treating claim as unsupported", {
|
|
53
|
+
claim: claimText,
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
});
|
|
56
|
+
return { claim: claimText, verdict: "neutral", score: 0, evidence: [] };
|
|
57
|
+
}
|
|
58
|
+
const best = pickDecisive(judgements, this.options.decisiveMinConfidence ?? DEFAULT_DECISIVE_MIN_CONFIDENCE);
|
|
59
|
+
let label = best.label;
|
|
60
|
+
let confidence = best.confidence;
|
|
61
|
+
// Precision guard against entity-substitution false positives: only ground a
|
|
62
|
+
// claim if its distinctive terms actually appear in the supporting passage.
|
|
63
|
+
if (label === "supported" && !salientTermsPresent(claimText, passages[best.passageIndex]?.text ?? "")) {
|
|
64
|
+
logger.debug("Withholding grounding: the claim's distinctive terms are absent from the evidence", {
|
|
65
|
+
claim: claimText,
|
|
66
|
+
});
|
|
67
|
+
label = "neutral";
|
|
68
|
+
confidence = 0;
|
|
69
|
+
}
|
|
70
|
+
const evidence = label === "neutral" ? [] : buildEvidence(label, judgements, relevant);
|
|
71
|
+
return {
|
|
72
|
+
claim: claimText,
|
|
73
|
+
verdict: label,
|
|
74
|
+
score: claimScore(label, confidence),
|
|
75
|
+
evidence,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The decisive judgement for a claim: the strongest non-neutral judgement that
|
|
81
|
+
* clears `minConfidence`. If none does, the claim is treated as unsupported
|
|
82
|
+
* (neutral) — a low-confidence "supported"/"contradicted" must not be enough to
|
|
83
|
+
* ground or refute a claim.
|
|
84
|
+
*/
|
|
85
|
+
export function pickDecisive(judgements, minConfidence) {
|
|
86
|
+
// Judgements arrive in retrieval-relevance order (most relevant first). The
|
|
87
|
+
// most relevant passage that gives a confident non-neutral verdict decides — a
|
|
88
|
+
// spurious verdict from a less-relevant passage must not override it.
|
|
89
|
+
for (const judgement of judgements) {
|
|
90
|
+
if (judgement.label !== "neutral" && judgement.confidence >= minConfidence) {
|
|
91
|
+
return judgement;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const first = judgements[0];
|
|
95
|
+
return first === undefined
|
|
96
|
+
? { passageIndex: 0, label: "neutral", confidence: 0 }
|
|
97
|
+
: { passageIndex: first.passageIndex, label: "neutral", confidence: first.confidence };
|
|
98
|
+
}
|
|
99
|
+
/** Strips trailing Obsidian block ids and carriage returns from passage text. */
|
|
100
|
+
function cleanText(text) {
|
|
101
|
+
return text.replace(/\r/g, "").replace(/\s*\^[A-Za-z0-9-]+\s*$/, "").trim();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Precision guard against entity-substitution false positives. A small NLI model
|
|
105
|
+
* can confidently "support" "We use MongoDB" from a passage that says PostgreSQL,
|
|
106
|
+
* because it reasons topically. Anchor only grounds a claim when the claim's
|
|
107
|
+
* distinctive terms — tech identifiers, acronyms, numbers, proper nouns — appear
|
|
108
|
+
* in the supporting passage. Conservative by design: a false refusal is far safer
|
|
109
|
+
* than a false grounding for a tool whose promise is "can't lie about your notes".
|
|
110
|
+
*/
|
|
111
|
+
function salientTermsPresent(claim, passage) {
|
|
112
|
+
const haystack = passage.toLowerCase();
|
|
113
|
+
const words = haystack.match(/[a-z0-9]+/g) ?? [];
|
|
114
|
+
for (const term of salientTerms(claim)) {
|
|
115
|
+
const needle = term.toLowerCase();
|
|
116
|
+
if (haystack.includes(needle))
|
|
117
|
+
continue;
|
|
118
|
+
// Lenient stem match so "PostgreSQL" ↔ "Postgres" still counts as present.
|
|
119
|
+
if (needle.length >= 5 && words.some((word) => word.length >= 4 && needle.includes(word))) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/** Distinctive terms in a claim: tech identifiers, acronyms, numbers, proper nouns. */
|
|
127
|
+
function salientTerms(claim) {
|
|
128
|
+
const tokens = claim.match(/[A-Za-z0-9][A-Za-z0-9.$:%+/-]*/g) ?? [];
|
|
129
|
+
const terms = [];
|
|
130
|
+
tokens.forEach((raw, index) => {
|
|
131
|
+
const token = raw.replace(/[.$:%+/-]+$/, ""); // drop trailing punctuation ("10:00." → "10:00")
|
|
132
|
+
if (token.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
const hasDigit = /\d/.test(token);
|
|
135
|
+
const camelCase = /[A-Z][a-z]*[A-Z]/.test(token); // PostgreSQL, MongoDB
|
|
136
|
+
const acronym = /^[A-Z]{2,}$/.test(token); // JWT, AWS, EKS
|
|
137
|
+
const midProperNoun = index > 0 && /^[A-Z][a-z]{2,}$/.test(token); // ...Redis, ...Cassandra
|
|
138
|
+
if (hasDigit || camelCase || acronym || midProperNoun)
|
|
139
|
+
terms.push(token);
|
|
140
|
+
});
|
|
141
|
+
return terms;
|
|
142
|
+
}
|
|
143
|
+
/** Collects the strongest passages that share the winning label as evidence. */
|
|
144
|
+
function buildEvidence(label, judgements, candidates) {
|
|
145
|
+
const evidence = [];
|
|
146
|
+
const matching = judgements
|
|
147
|
+
.filter((judgement) => judgement.label === label && judgement.confidence >= 0.4)
|
|
148
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
149
|
+
.slice(0, 3);
|
|
150
|
+
for (const judgement of matching) {
|
|
151
|
+
const chunk = candidates[judgement.passageIndex];
|
|
152
|
+
if (chunk === undefined)
|
|
153
|
+
continue;
|
|
154
|
+
evidence.push({
|
|
155
|
+
path: chunk.notePath,
|
|
156
|
+
anchor: chunk.anchor,
|
|
157
|
+
blockId: chunk.blockId,
|
|
158
|
+
heading: chunk.headingPath,
|
|
159
|
+
quote: cleanText(chunk.text),
|
|
160
|
+
score: Math.round(judgement.confidence * 1000) / 1000,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return evidence;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/verify/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export const DEFAULT_THRESHOLDS = { grounded: 0.8, refuse: 0.5 };
|
|
2
|
+
/** Maps a per-claim label + confidence to a 0..1 grounding score. */
|
|
3
|
+
export function claimScore(verdict, confidence) {
|
|
4
|
+
// Only positive support earns score; neutral (no evidence) and contradicted
|
|
5
|
+
// (refuted by the notes) both score 0.
|
|
6
|
+
return verdict === "supported" ? confidence : 0;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Combines per-claim verdicts into an overall result. A single contradiction
|
|
10
|
+
* caps the answer at "refused" — a note refuting a claim is worse than a claim
|
|
11
|
+
* merely lacking support, and is the core "caught a lie" signal.
|
|
12
|
+
*/
|
|
13
|
+
export function aggregate(perClaim, thresholds = DEFAULT_THRESHOLDS) {
|
|
14
|
+
if (perClaim.length === 0) {
|
|
15
|
+
return {
|
|
16
|
+
grounded: false,
|
|
17
|
+
score: 0,
|
|
18
|
+
verdict: "refused",
|
|
19
|
+
summary: "No verifiable claims were found in the text.",
|
|
20
|
+
perClaim: [],
|
|
21
|
+
refusedClaims: [],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const mean = perClaim.reduce((sum, claim) => sum + claim.score, 0) / perClaim.length;
|
|
25
|
+
const hasContradiction = perClaim.some((claim) => claim.verdict === "contradicted");
|
|
26
|
+
const allGrounded = perClaim.every((claim) => claim.verdict === "supported" && claim.score >= thresholds.grounded);
|
|
27
|
+
const refusedClaims = perClaim
|
|
28
|
+
.filter((claim) => claim.verdict === "contradicted" || claim.score < thresholds.refuse)
|
|
29
|
+
.map((claim) => claim.claim);
|
|
30
|
+
let verdict;
|
|
31
|
+
if (allGrounded && !hasContradiction) {
|
|
32
|
+
verdict = "grounded";
|
|
33
|
+
}
|
|
34
|
+
else if (hasContradiction || mean < thresholds.refuse) {
|
|
35
|
+
verdict = "refused";
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
verdict = "flagged";
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
grounded: verdict === "grounded",
|
|
42
|
+
score: round(mean),
|
|
43
|
+
verdict,
|
|
44
|
+
summary: summarize(perClaim),
|
|
45
|
+
perClaim,
|
|
46
|
+
refusedClaims,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function summarize(perClaim) {
|
|
50
|
+
const total = perClaim.length;
|
|
51
|
+
const supported = perClaim.filter((claim) => claim.verdict === "supported").length;
|
|
52
|
+
const contradicted = perClaim.filter((claim) => claim.verdict === "contradicted").length;
|
|
53
|
+
const unsupported = perClaim.filter((claim) => claim.verdict === "neutral").length;
|
|
54
|
+
const parts = [`${supported}/${total} claim(s) supported by your notes`];
|
|
55
|
+
if (contradicted > 0)
|
|
56
|
+
parts.push(`${contradicted} contradicted`);
|
|
57
|
+
if (unsupported > 0)
|
|
58
|
+
parts.push(`${unsupported} unsupported`);
|
|
59
|
+
return `${parts.join("; ")}.`;
|
|
60
|
+
}
|
|
61
|
+
function round(value) {
|
|
62
|
+
return Math.round(value * 1000) / 1000;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/verify/score.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,kBAAkB,GAAe,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAE7E,qEAAqE;AACrE,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,UAAkB;IAC7D,4EAA4E;IAC5E,uCAAuC;IACvC,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,QAA2B,EAC3B,aAAyB,kBAAkB;IAE3C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,8CAA8C;YACvD,QAAQ,EAAE,EAAE;YACZ,aAAa,EAAE,EAAE;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IACrF,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,cAAc,CAAC,CAAC;IACpF,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAChC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,IAAI,UAAU,CAAC,QAAQ,CAC/E,CAAC;IACF,MAAM,aAAa,GAAG,QAAQ;SAC3B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,cAAc,IAAI,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;SACtF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE/B,IAAI,OAAuB,CAAC;IAC5B,IAAI,WAAW,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrC,OAAO,GAAG,UAAU,CAAC;IACvB,CAAC;SAAM,IAAI,gBAAgB,IAAI,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QACxD,OAAO,GAAG,SAAS,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,SAAS,CAAC;IACtB,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,OAAO,KAAK,UAAU;QAChC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;QAClB,OAAO;QACP,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC;QAC5B,QAAQ;QACR,aAAa;KACd,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,QAA2B;IAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC9B,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IACnF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,cAAc,CAAC,CAAC,MAAM,CAAC;IACzF,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAEnF,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,IAAI,KAAK,mCAAmC,CAAC,CAAC;IACzE,IAAI,YAAY,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,eAAe,CAAC,CAAC;IACjE,IAAI,WAAW,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,cAAc,CAAC,CAAC;IAC9D,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAChC,CAAC;AAED,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/verify/types.ts"],"names":[],"mappings":"AAAA,gCAAgC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../src/verify/verifier.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obsidian-anchor",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A reliability-first MCP server for Obsidian that refuses AI claims your notes can't support.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"obsidian-anchor": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Justin",
|
|
14
|
+
"homepage": "https://github.com/Perfectio/obsidian-anchor#readme",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Perfectio/obsidian-anchor.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/Perfectio/obsidian-anchor/issues"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"dev": "tsx src/index.ts",
|
|
30
|
+
"start": "node dist/index.js",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"eval": "tsx eval/run-eval.ts",
|
|
34
|
+
"eval:e2e": "tsx eval/run-e2e-eval.ts",
|
|
35
|
+
"eval:ko": "tsx eval/run-e2e-eval.ts eval/fixtures/vault-ko eval/ko-dataset.jsonl",
|
|
36
|
+
"lint": "eslint .",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"check:package": "publint",
|
|
39
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm run lint && npm test"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"mcp",
|
|
43
|
+
"model-context-protocol",
|
|
44
|
+
"obsidian",
|
|
45
|
+
"grounding",
|
|
46
|
+
"rag",
|
|
47
|
+
"hallucination",
|
|
48
|
+
"fact-checking",
|
|
49
|
+
"ai"
|
|
50
|
+
],
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@anthropic-ai/sdk": "^0.103.0",
|
|
53
|
+
"@huggingface/transformers": "^4.2.0",
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
55
|
+
"better-sqlite3": "^12.10.0",
|
|
56
|
+
"chokidar": "^4.0.3",
|
|
57
|
+
"sqlite-vec": "^0.1.9",
|
|
58
|
+
"zod": "^4.4.3"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@eslint/js": "^10.0.1",
|
|
62
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
63
|
+
"@types/node": "^25.9.2",
|
|
64
|
+
"eslint": "^10.4.1",
|
|
65
|
+
"publint": "^0.3.0",
|
|
66
|
+
"tsx": "^4.22.4",
|
|
67
|
+
"typescript": "^6.0.3",
|
|
68
|
+
"typescript-eslint": "^8.61.0",
|
|
69
|
+
"vitest": "^4.1.8"
|
|
70
|
+
}
|
|
71
|
+
}
|