ollama-intern-mcp 1.0.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 +252 -0
- package/SECURITY.md +22 -0
- package/dist/corpus/chunker.d.ts +63 -0
- package/dist/corpus/chunker.d.ts.map +1 -0
- package/dist/corpus/chunker.js +230 -0
- package/dist/corpus/chunker.js.map +1 -0
- package/dist/corpus/fusion.d.ts +67 -0
- package/dist/corpus/fusion.d.ts.map +1 -0
- package/dist/corpus/fusion.js +76 -0
- package/dist/corpus/fusion.js.map +1 -0
- package/dist/corpus/indexer.d.ts +33 -0
- package/dist/corpus/indexer.d.ts.map +1 -0
- package/dist/corpus/indexer.js +201 -0
- package/dist/corpus/indexer.js.map +1 -0
- package/dist/corpus/lexical.d.ts +95 -0
- package/dist/corpus/lexical.d.ts.map +1 -0
- package/dist/corpus/lexical.js +227 -0
- package/dist/corpus/lexical.js.map +1 -0
- package/dist/corpus/manifest.d.ts +29 -0
- package/dist/corpus/manifest.d.ts.map +1 -0
- package/dist/corpus/manifest.js +45 -0
- package/dist/corpus/manifest.js.map +1 -0
- package/dist/corpus/refresh.d.ts +54 -0
- package/dist/corpus/refresh.d.ts.map +1 -0
- package/dist/corpus/refresh.js +155 -0
- package/dist/corpus/refresh.js.map +1 -0
- package/dist/corpus/searcher.d.ts +48 -0
- package/dist/corpus/searcher.d.ts.map +1 -0
- package/dist/corpus/searcher.js +131 -0
- package/dist/corpus/searcher.js.map +1 -0
- package/dist/corpus/storage.d.ts +56 -0
- package/dist/corpus/storage.d.ts.map +1 -0
- package/dist/corpus/storage.js +81 -0
- package/dist/corpus/storage.js.map +1 -0
- package/dist/coverage.d.ts +38 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +117 -0
- package/dist/coverage.js.map +1 -0
- package/dist/embedMath.d.ts +22 -0
- package/dist/embedMath.d.ts.map +1 -0
- package/dist/embedMath.js +34 -0
- package/dist/embedMath.js.map +1 -0
- package/dist/envelope.d.ts +53 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +32 -0
- package/dist/envelope.js.map +1 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +30 -0
- package/dist/errors.js.map +1 -0
- package/dist/evals/retrieval.d.ts +68 -0
- package/dist/evals/retrieval.d.ts.map +1 -0
- package/dist/evals/retrieval.js +173 -0
- package/dist/evals/retrieval.js.map +1 -0
- package/dist/guardrails/citations.d.ts +28 -0
- package/dist/guardrails/citations.d.ts.map +1 -0
- package/dist/guardrails/citations.js +45 -0
- package/dist/guardrails/citations.js.map +1 -0
- package/dist/guardrails/compileCheck.d.ts +20 -0
- package/dist/guardrails/compileCheck.d.ts.map +1 -0
- package/dist/guardrails/compileCheck.js +96 -0
- package/dist/guardrails/compileCheck.js.map +1 -0
- package/dist/guardrails/confidence.d.ts +21 -0
- package/dist/guardrails/confidence.d.ts.map +1 -0
- package/dist/guardrails/confidence.js +18 -0
- package/dist/guardrails/confidence.js.map +1 -0
- package/dist/guardrails/timeouts.d.ts +35 -0
- package/dist/guardrails/timeouts.d.ts.map +1 -0
- package/dist/guardrails/timeouts.js +58 -0
- package/dist/guardrails/timeouts.js.map +1 -0
- package/dist/guardrails/writeConfirm.d.ts +25 -0
- package/dist/guardrails/writeConfirm.d.ts.map +1 -0
- package/dist/guardrails/writeConfirm.js +35 -0
- package/dist/guardrails/writeConfirm.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +153 -0
- package/dist/index.js.map +1 -0
- package/dist/observability.d.ts +66 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +51 -0
- package/dist/observability.js.map +1 -0
- package/dist/ollama.d.ts +105 -0
- package/dist/ollama.d.ts.map +1 -0
- package/dist/ollama.js +109 -0
- package/dist/ollama.js.map +1 -0
- package/dist/prewarm.d.ts +28 -0
- package/dist/prewarm.d.ts.map +1 -0
- package/dist/prewarm.js +69 -0
- package/dist/prewarm.js.map +1 -0
- package/dist/profiles.d.ts +54 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +95 -0
- package/dist/profiles.js.map +1 -0
- package/dist/protectedPaths.d.ts +33 -0
- package/dist/protectedPaths.d.ts.map +1 -0
- package/dist/protectedPaths.js +54 -0
- package/dist/protectedPaths.js.map +1 -0
- package/dist/runContext.d.ts +21 -0
- package/dist/runContext.d.ts.map +1 -0
- package/dist/runContext.js +9 -0
- package/dist/runContext.js.map +1 -0
- package/dist/semaphore.d.ts +16 -0
- package/dist/semaphore.d.ts.map +1 -0
- package/dist/semaphore.js +37 -0
- package/dist/semaphore.js.map +1 -0
- package/dist/sources.d.ts +19 -0
- package/dist/sources.d.ts.map +1 -0
- package/dist/sources.js +41 -0
- package/dist/sources.js.map +1 -0
- package/dist/tiers.d.ts +39 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +44 -0
- package/dist/tiers.js.map +1 -0
- package/dist/tools/artifactDiff.d.ts +66 -0
- package/dist/tools/artifactDiff.d.ts.map +1 -0
- package/dist/tools/artifactDiff.js +66 -0
- package/dist/tools/artifactDiff.js.map +1 -0
- package/dist/tools/artifactExportToPath.d.ts +38 -0
- package/dist/tools/artifactExportToPath.d.ts.map +1 -0
- package/dist/tools/artifactExportToPath.js +51 -0
- package/dist/tools/artifactExportToPath.js.map +1 -0
- package/dist/tools/artifactList.d.ts +60 -0
- package/dist/tools/artifactList.d.ts.map +1 -0
- package/dist/tools/artifactList.js +107 -0
- package/dist/tools/artifactList.js.map +1 -0
- package/dist/tools/artifactRead.d.ts +43 -0
- package/dist/tools/artifactRead.d.ts.map +1 -0
- package/dist/tools/artifactRead.js +83 -0
- package/dist/tools/artifactRead.js.map +1 -0
- package/dist/tools/artifactSnippets.d.ts +56 -0
- package/dist/tools/artifactSnippets.d.ts.map +1 -0
- package/dist/tools/artifactSnippets.js +97 -0
- package/dist/tools/artifactSnippets.js.map +1 -0
- package/dist/tools/artifacts/diff.d.ts +161 -0
- package/dist/tools/artifacts/diff.d.ts.map +1 -0
- package/dist/tools/artifacts/diff.js +267 -0
- package/dist/tools/artifacts/diff.js.map +1 -0
- package/dist/tools/artifacts/export.d.ts +43 -0
- package/dist/tools/artifacts/export.d.ts.map +1 -0
- package/dist/tools/artifacts/export.js +113 -0
- package/dist/tools/artifacts/export.js.map +1 -0
- package/dist/tools/artifacts/scan.d.ts +86 -0
- package/dist/tools/artifacts/scan.d.ts.map +1 -0
- package/dist/tools/artifacts/scan.js +251 -0
- package/dist/tools/artifacts/scan.js.map +1 -0
- package/dist/tools/artifacts/snippets.d.ts +42 -0
- package/dist/tools/artifacts/snippets.d.ts.map +1 -0
- package/dist/tools/artifacts/snippets.js +165 -0
- package/dist/tools/artifacts/snippets.js.map +1 -0
- package/dist/tools/batch.d.ts +65 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +117 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/briefs/common.d.ts +65 -0
- package/dist/tools/briefs/common.d.ts.map +1 -0
- package/dist/tools/briefs/common.js +128 -0
- package/dist/tools/briefs/common.js.map +1 -0
- package/dist/tools/briefs/evidence.d.ts +40 -0
- package/dist/tools/briefs/evidence.d.ts.map +1 -0
- package/dist/tools/briefs/evidence.js +103 -0
- package/dist/tools/briefs/evidence.js.map +1 -0
- package/dist/tools/changeBrief.d.ts +85 -0
- package/dist/tools/changeBrief.d.ts.map +1 -0
- package/dist/tools/changeBrief.js +228 -0
- package/dist/tools/changeBrief.js.map +1 -0
- package/dist/tools/chat.d.ts +42 -0
- package/dist/tools/chat.d.ts.map +1 -0
- package/dist/tools/chat.js +49 -0
- package/dist/tools/chat.js.map +1 -0
- package/dist/tools/classify.d.ts +56 -0
- package/dist/tools/classify.d.ts.map +1 -0
- package/dist/tools/classify.js +103 -0
- package/dist/tools/classify.js.map +1 -0
- package/dist/tools/corpusAnswer.d.ts +79 -0
- package/dist/tools/corpusAnswer.d.ts.map +1 -0
- package/dist/tools/corpusAnswer.js +259 -0
- package/dist/tools/corpusAnswer.js.map +1 -0
- package/dist/tools/corpusIndex.d.ts +30 -0
- package/dist/tools/corpusIndex.d.ts.map +1 -0
- package/dist/tools/corpusIndex.js +67 -0
- package/dist/tools/corpusIndex.js.map +1 -0
- package/dist/tools/corpusList.d.ts +19 -0
- package/dist/tools/corpusList.d.ts.map +1 -0
- package/dist/tools/corpusList.js +34 -0
- package/dist/tools/corpusList.js.map +1 -0
- package/dist/tools/corpusRefresh.d.ts +29 -0
- package/dist/tools/corpusRefresh.d.ts.map +1 -0
- package/dist/tools/corpusRefresh.js +58 -0
- package/dist/tools/corpusRefresh.js.map +1 -0
- package/dist/tools/corpusSearch.d.ts +44 -0
- package/dist/tools/corpusSearch.d.ts.map +1 -0
- package/dist/tools/corpusSearch.js +80 -0
- package/dist/tools/corpusSearch.js.map +1 -0
- package/dist/tools/draft.d.ts +38 -0
- package/dist/tools/draft.d.ts.map +1 -0
- package/dist/tools/draft.js +72 -0
- package/dist/tools/draft.js.map +1 -0
- package/dist/tools/embed.d.ts +27 -0
- package/dist/tools/embed.d.ts.map +1 -0
- package/dist/tools/embed.js +41 -0
- package/dist/tools/embed.js.map +1 -0
- package/dist/tools/embedSearch.d.ts +58 -0
- package/dist/tools/embedSearch.d.ts.map +1 -0
- package/dist/tools/embedSearch.js +76 -0
- package/dist/tools/embedSearch.js.map +1 -0
- package/dist/tools/extract.d.ts +58 -0
- package/dist/tools/extract.d.ts.map +1 -0
- package/dist/tools/extract.js +93 -0
- package/dist/tools/extract.js.map +1 -0
- package/dist/tools/incidentBrief.d.ts +81 -0
- package/dist/tools/incidentBrief.d.ts.map +1 -0
- package/dist/tools/incidentBrief.js +252 -0
- package/dist/tools/incidentBrief.js.map +1 -0
- package/dist/tools/packs/changePack.d.ts +166 -0
- package/dist/tools/packs/changePack.d.ts.map +1 -0
- package/dist/tools/packs/changePack.js +470 -0
- package/dist/tools/packs/changePack.js.map +1 -0
- package/dist/tools/packs/incidentPack.d.ts +125 -0
- package/dist/tools/packs/incidentPack.d.ts.map +1 -0
- package/dist/tools/packs/incidentPack.js +354 -0
- package/dist/tools/packs/incidentPack.js.map +1 -0
- package/dist/tools/packs/repoPack.d.ts +207 -0
- package/dist/tools/packs/repoPack.d.ts.map +1 -0
- package/dist/tools/packs/repoPack.js +456 -0
- package/dist/tools/packs/repoPack.js.map +1 -0
- package/dist/tools/repoBrief.d.ts +81 -0
- package/dist/tools/repoBrief.d.ts.map +1 -0
- package/dist/tools/repoBrief.js +213 -0
- package/dist/tools/repoBrief.js.map +1 -0
- package/dist/tools/research.d.ts +45 -0
- package/dist/tools/research.d.ts.map +1 -0
- package/dist/tools/research.js +100 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/tools/runner.d.ts +24 -0
- package/dist/tools/runner.d.ts.map +1 -0
- package/dist/tools/runner.js +47 -0
- package/dist/tools/runner.js.map +1 -0
- package/dist/tools/summarizeDeep.d.ts +49 -0
- package/dist/tools/summarizeDeep.d.ts.map +1 -0
- package/dist/tools/summarizeDeep.js +109 -0
- package/dist/tools/summarizeDeep.js.map +1 -0
- package/dist/tools/summarizeFast.d.ts +28 -0
- package/dist/tools/summarizeFast.d.ts.map +1 -0
- package/dist/tools/summarizeFast.js +43 -0
- package/dist/tools/summarizeFast.js.map +1 -0
- package/dist/tools/triageLogs.d.ts +51 -0
- package/dist/tools/triageLogs.d.ts.map +1 -0
- package/dist/tools/triageLogs.js +96 -0
- package/dist/tools/triageLogs.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieval eval runner — measurement law for the Retrieval Truth Spine.
|
|
3
|
+
*
|
|
4
|
+
* Loads gold queries from evals/gold/retrieval.jsonl, runs every query
|
|
5
|
+
* through every search mode against a fixture corpus, and reports
|
|
6
|
+
* precision@1 and precision@3 per (mode × class) plus per-mode and
|
|
7
|
+
* overall aggregates.
|
|
8
|
+
*
|
|
9
|
+
* The runner is deliberately retrieval-native: assertions check whether
|
|
10
|
+
* the expected path appears in the top-K hits, not whether the answer
|
|
11
|
+
* is correct. Answer synthesis is slice 5; measurement is the floor
|
|
12
|
+
* under it.
|
|
13
|
+
*
|
|
14
|
+
* No product-surface changes ride on this file. It is a pure consumer
|
|
15
|
+
* of searchCorpus() and the corpus schema.
|
|
16
|
+
*/
|
|
17
|
+
import { readFile } from "node:fs/promises";
|
|
18
|
+
import { basename } from "node:path";
|
|
19
|
+
import { searchCorpus, SEARCH_MODES } from "../corpus/searcher.js";
|
|
20
|
+
export const QUERY_CLASSES = [
|
|
21
|
+
"semantic",
|
|
22
|
+
"fact",
|
|
23
|
+
"procedural",
|
|
24
|
+
"confusable",
|
|
25
|
+
];
|
|
26
|
+
export async function loadGold(filePath) {
|
|
27
|
+
const raw = await readFile(filePath, "utf8");
|
|
28
|
+
const lines = raw.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
29
|
+
const queries = [];
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const q = JSON.parse(line);
|
|
32
|
+
if (!QUERY_CLASSES.includes(q.class)) {
|
|
33
|
+
throw new Error(`retrieval.jsonl: unknown class "${q.class}" on ${q.id}`);
|
|
34
|
+
}
|
|
35
|
+
queries.push(q);
|
|
36
|
+
}
|
|
37
|
+
return queries;
|
|
38
|
+
}
|
|
39
|
+
function pathMatches(hitPath, expected) {
|
|
40
|
+
// Expected paths in gold are basenames; hit paths are absolute. Compare by basename.
|
|
41
|
+
return basename(hitPath) === basename(expected);
|
|
42
|
+
}
|
|
43
|
+
function anyExpectedInTop(hits, expected, n) {
|
|
44
|
+
for (let i = 0; i < Math.min(n, hits.length); i++) {
|
|
45
|
+
for (const ex of expected) {
|
|
46
|
+
if (pathMatches(hits[i].path, ex))
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function phrasesSatisfied(hits, expected, phrases) {
|
|
53
|
+
// Find every hit whose path matches an expected doc, and check
|
|
54
|
+
// whether any one of those hits' text contains all phrases.
|
|
55
|
+
for (const hit of hits) {
|
|
56
|
+
if (!expected.some((e) => pathMatches(hit.path, e)))
|
|
57
|
+
continue;
|
|
58
|
+
// Use preview text if available; otherwise we'd need the chunk store.
|
|
59
|
+
// Runner always passes preview_chars so this is populated.
|
|
60
|
+
const text = (hit.preview ?? "").toLowerCase();
|
|
61
|
+
if (phrases.every((p) => text.includes(p.toLowerCase())))
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
export async function runRetrievalEval(opts) {
|
|
67
|
+
const topK = opts.topK ?? 3;
|
|
68
|
+
const previewChars = opts.previewChars ?? 400;
|
|
69
|
+
const records = [];
|
|
70
|
+
for (const q of opts.gold) {
|
|
71
|
+
for (const mode of SEARCH_MODES) {
|
|
72
|
+
const hits = await searchCorpus({
|
|
73
|
+
corpus: opts.corpus,
|
|
74
|
+
query: q.query,
|
|
75
|
+
model: opts.model,
|
|
76
|
+
mode,
|
|
77
|
+
top_k: topK,
|
|
78
|
+
preview_chars: previewChars,
|
|
79
|
+
client: opts.client,
|
|
80
|
+
});
|
|
81
|
+
const hit1 = anyExpectedInTop(hits, q.expected_paths, 1);
|
|
82
|
+
const hit3 = anyExpectedInTop(hits, q.expected_paths, 3);
|
|
83
|
+
const phrasesHit = q.expected_phrases && q.expected_phrases.length > 0
|
|
84
|
+
? phrasesSatisfied(hits, q.expected_paths, q.expected_phrases)
|
|
85
|
+
: null;
|
|
86
|
+
records.push({
|
|
87
|
+
id: q.id,
|
|
88
|
+
class: q.class,
|
|
89
|
+
query: q.query,
|
|
90
|
+
mode,
|
|
91
|
+
top_paths: hits.map((h) => basename(h.path)),
|
|
92
|
+
hit1,
|
|
93
|
+
hit3,
|
|
94
|
+
phrasesHit,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return records;
|
|
99
|
+
}
|
|
100
|
+
function emptyCell() {
|
|
101
|
+
return { n: 0, precision1: 0, precision3: 0 };
|
|
102
|
+
}
|
|
103
|
+
function accumulate(cell, r) {
|
|
104
|
+
cell.n += 1;
|
|
105
|
+
cell.precision1 += r.hit1 ? 1 : 0;
|
|
106
|
+
cell.precision3 += r.hit3 ? 1 : 0;
|
|
107
|
+
}
|
|
108
|
+
function finalize(cell) {
|
|
109
|
+
if (cell.n === 0)
|
|
110
|
+
return cell;
|
|
111
|
+
return {
|
|
112
|
+
n: cell.n,
|
|
113
|
+
precision1: cell.precision1 / cell.n,
|
|
114
|
+
precision3: cell.precision3 / cell.n,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function summarizeEval(records) {
|
|
118
|
+
const overall = emptyCell();
|
|
119
|
+
const byMode = {};
|
|
120
|
+
const byModeByClass = {};
|
|
121
|
+
for (const m of SEARCH_MODES) {
|
|
122
|
+
byMode[m] = emptyCell();
|
|
123
|
+
byModeByClass[m] = {};
|
|
124
|
+
for (const c of QUERY_CLASSES)
|
|
125
|
+
byModeByClass[m][c] = emptyCell();
|
|
126
|
+
}
|
|
127
|
+
for (const r of records) {
|
|
128
|
+
accumulate(overall, r);
|
|
129
|
+
accumulate(byMode[r.mode], r);
|
|
130
|
+
accumulate(byModeByClass[r.mode][r.class], r);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
overall: finalize(overall),
|
|
134
|
+
byMode: Object.fromEntries(Object.entries(byMode).map(([k, v]) => [k, finalize(v)])),
|
|
135
|
+
byModeByClass: Object.fromEntries(Object.entries(byModeByClass).map(([m, classes]) => [
|
|
136
|
+
m,
|
|
137
|
+
Object.fromEntries(Object.entries(classes).map(([c, v]) => [c, finalize(v)])),
|
|
138
|
+
])),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function pct(x) {
|
|
142
|
+
return (x * 100).toFixed(0).padStart(3, " ") + "%";
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Markdown-friendly report suitable for console.log during tests. The
|
|
146
|
+
* shape is stable so it can be diffed between runs.
|
|
147
|
+
*/
|
|
148
|
+
export function formatEvalReport(summary) {
|
|
149
|
+
const lines = [];
|
|
150
|
+
lines.push("");
|
|
151
|
+
lines.push("## Retrieval eval — precision@1 / precision@3");
|
|
152
|
+
lines.push("");
|
|
153
|
+
lines.push("Per mode × class (n = queries in that class):");
|
|
154
|
+
lines.push("");
|
|
155
|
+
const header = ["mode".padEnd(11), ...QUERY_CLASSES.map((c) => c.padStart(14)), "overall".padStart(14)].join(" | ");
|
|
156
|
+
lines.push(header);
|
|
157
|
+
lines.push("-".repeat(header.length));
|
|
158
|
+
for (const mode of SEARCH_MODES) {
|
|
159
|
+
const cells = [mode.padEnd(11)];
|
|
160
|
+
for (const c of QUERY_CLASSES) {
|
|
161
|
+
const m = summary.byModeByClass[mode][c];
|
|
162
|
+
cells.push(`${pct(m.precision1)}/${pct(m.precision3)}`.padStart(14));
|
|
163
|
+
}
|
|
164
|
+
const o = summary.byMode[mode];
|
|
165
|
+
cells.push(`${pct(o.precision1)}/${pct(o.precision3)}`.padStart(14));
|
|
166
|
+
lines.push(cells.join(" | "));
|
|
167
|
+
}
|
|
168
|
+
lines.push("");
|
|
169
|
+
lines.push(`Overall across all modes: P@1 ${pct(summary.overall.precision1)}, P@3 ${pct(summary.overall.precision3)} (n=${summary.overall.n})`);
|
|
170
|
+
lines.push("");
|
|
171
|
+
return lines.join("\n");
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=retrieval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieval.js","sourceRoot":"","sources":["../../src/evals/retrieval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation guardrail — `ollama_research` must only cite paths that were
|
|
3
|
+
* provided in its source_paths input. Anything else is a fabrication and
|
|
4
|
+
* gets stripped server-side before returning to the caller.
|
|
5
|
+
*/
|
|
6
|
+
export interface RawCitation {
|
|
7
|
+
path: string;
|
|
8
|
+
line_range?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ValidatedCitation {
|
|
11
|
+
path: string;
|
|
12
|
+
line_range?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CitationValidationResult {
|
|
15
|
+
valid: ValidatedCitation[];
|
|
16
|
+
stripped: RawCitation[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Strip any citation whose path is not in allowedPaths.
|
|
20
|
+
* Comparison is on normalized paths — forward slashes, no leading ./.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateCitations(citations: RawCitation[], allowedPaths: string[]): CitationValidationResult;
|
|
23
|
+
/**
|
|
24
|
+
* Extract citations from a model's free-text "Sources:" block.
|
|
25
|
+
* Accepts formats like `path.md:10-25` or `path.md (lines 10-25)`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseCitations(text: string): RawCitation[];
|
|
28
|
+
//# sourceMappingURL=citations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"citations.d.ts","sourceRoot":"","sources":["../../src/guardrails/citations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,WAAW,EAAE,EACxB,YAAY,EAAE,MAAM,EAAE,GACrB,wBAAwB,CAY1B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAe1D"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation guardrail — `ollama_research` must only cite paths that were
|
|
3
|
+
* provided in its source_paths input. Anything else is a fabrication and
|
|
4
|
+
* gets stripped server-side before returning to the caller.
|
|
5
|
+
*/
|
|
6
|
+
import { normalizePath } from "../protectedPaths.js";
|
|
7
|
+
/**
|
|
8
|
+
* Strip any citation whose path is not in allowedPaths.
|
|
9
|
+
* Comparison is on normalized paths — forward slashes, no leading ./.
|
|
10
|
+
*/
|
|
11
|
+
export function validateCitations(citations, allowedPaths) {
|
|
12
|
+
const allowed = new Set(allowedPaths.map(normalizePath));
|
|
13
|
+
const valid = [];
|
|
14
|
+
const stripped = [];
|
|
15
|
+
for (const c of citations) {
|
|
16
|
+
if (allowed.has(normalizePath(c.path))) {
|
|
17
|
+
valid.push({ path: c.path, ...(c.line_range ? { line_range: c.line_range } : {}) });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
stripped.push(c);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { valid, stripped };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract citations from a model's free-text "Sources:" block.
|
|
27
|
+
* Accepts formats like `path.md:10-25` or `path.md (lines 10-25)`.
|
|
28
|
+
*/
|
|
29
|
+
export function parseCitations(text) {
|
|
30
|
+
const lines = text.split("\n");
|
|
31
|
+
const citations = [];
|
|
32
|
+
const pathLine = /^[-*\s]*([^\s:()]+(?:\.[a-z0-9]+)?)(?::(\d+(?:-\d+)?)|\s*\(lines?\s+(\d+(?:-\d+)?)\))?/i;
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
const m = line.match(pathLine);
|
|
35
|
+
if (m) {
|
|
36
|
+
const path = m[1];
|
|
37
|
+
const range = m[2] || m[3];
|
|
38
|
+
if (path && (path.includes("/") || path.includes("."))) {
|
|
39
|
+
citations.push({ path, ...(range ? { line_range: range } : {}) });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return citations;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=citations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"citations.js","sourceRoot":"","sources":["../../src/guardrails/citations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAiBrD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAwB,EACxB,YAAsB;IAEtB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IACzD,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,yFAAyF,CAAC;IAC3G,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACvD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile check — after ollama_draft produces code, run a cheap checker
|
|
3
|
+
* and report {compiles, checker, stderr_tail} on the envelope.
|
|
4
|
+
*
|
|
5
|
+
* The stderr tail is the real value: a boolean tells you something broke,
|
|
6
|
+
* the tail tells you *what* so review is useful instead of just binary.
|
|
7
|
+
*
|
|
8
|
+
* Never throws — compile failure is data, not an error. The tool handler
|
|
9
|
+
* still returns the draft; the reviewer decides what to do.
|
|
10
|
+
*/
|
|
11
|
+
export type SupportedLanguage = "typescript" | "javascript" | "python" | "rust" | "go";
|
|
12
|
+
export interface CompileCheckResult {
|
|
13
|
+
compiles: boolean;
|
|
14
|
+
checker: string;
|
|
15
|
+
stderr_tail: string;
|
|
16
|
+
skipped?: boolean;
|
|
17
|
+
skip_reason?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function compileCheck(code: string, language: string | undefined): Promise<CompileCheckResult>;
|
|
20
|
+
//# sourceMappingURL=compileCheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compileCheck.d.ts","sourceRoot":"","sources":["../../src/guardrails/compileCheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC;AAEvF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAuB7B"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile check — after ollama_draft produces code, run a cheap checker
|
|
3
|
+
* and report {compiles, checker, stderr_tail} on the envelope.
|
|
4
|
+
*
|
|
5
|
+
* The stderr tail is the real value: a boolean tells you something broke,
|
|
6
|
+
* the tail tells you *what* so review is useful instead of just binary.
|
|
7
|
+
*
|
|
8
|
+
* Never throws — compile failure is data, not an error. The tool handler
|
|
9
|
+
* still returns the draft; the reviewer decides what to do.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
/** Last ~800 chars of stderr — enough for the interesting part without flooding the envelope. */
|
|
16
|
+
const STDERR_TAIL_CHARS = 800;
|
|
17
|
+
const CHECKER_TIMEOUT_MS = 15_000;
|
|
18
|
+
export async function compileCheck(code, language) {
|
|
19
|
+
if (!language) {
|
|
20
|
+
return { compiles: false, checker: "none", stderr_tail: "", skipped: true, skip_reason: "no language specified" };
|
|
21
|
+
}
|
|
22
|
+
const lang = language.toLowerCase();
|
|
23
|
+
switch (lang) {
|
|
24
|
+
case "typescript":
|
|
25
|
+
// `npx -p typescript -- tsc ...` tells npx WHICH package provides the tsc
|
|
26
|
+
// binary. The old `npx -y typescript tsc` form treats "tsc" as an arg to
|
|
27
|
+
// a nonexistent "typescript" binary and fails with "could not determine
|
|
28
|
+
// executable to run" — found in the first live dogfood run.
|
|
29
|
+
return runChecker(code, ".ts", "npx", ["-p", "typescript", "--", "tsc", "--noEmit", "--target", "ES2022", "--module", "ESNext", "--moduleResolution", "node"]);
|
|
30
|
+
case "javascript":
|
|
31
|
+
return runChecker(code, ".js", "node", ["--check"]);
|
|
32
|
+
case "python":
|
|
33
|
+
return runChecker(code, ".py", "python", ["-m", "py_compile"]);
|
|
34
|
+
case "rust":
|
|
35
|
+
return runChecker(code, ".rs", "rustc", ["--edition", "2021", "--emit=metadata", "-o", "/dev/null"]);
|
|
36
|
+
case "go":
|
|
37
|
+
return runChecker(code, ".go", "gofmt", ["-e"]);
|
|
38
|
+
default:
|
|
39
|
+
return { compiles: false, checker: "none", stderr_tail: "", skipped: true, skip_reason: `unsupported language: ${language}` };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function runChecker(code, ext, command, baseArgs) {
|
|
43
|
+
let dir = null;
|
|
44
|
+
try {
|
|
45
|
+
dir = await mkdtemp(join(tmpdir(), "intern-check-"));
|
|
46
|
+
const file = join(dir, `snippet${ext}`);
|
|
47
|
+
await writeFile(file, code, "utf8");
|
|
48
|
+
const args = [...baseArgs, file];
|
|
49
|
+
const { code: exitCode, stdout, stderr } = await runProcess(command, args, CHECKER_TIMEOUT_MS);
|
|
50
|
+
// tsc writes diagnostics to STDOUT, most other checkers to STDERR.
|
|
51
|
+
// Merge both so the tail is useful regardless of the tool's habits.
|
|
52
|
+
const diagnostics = [stderr, stdout].filter(Boolean).join("\n");
|
|
53
|
+
return {
|
|
54
|
+
compiles: exitCode === 0,
|
|
55
|
+
checker: `${command} ${baseArgs.join(" ")}`.trim(),
|
|
56
|
+
stderr_tail: tail(diagnostics, STDERR_TAIL_CHARS),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
compiles: false,
|
|
62
|
+
checker: command,
|
|
63
|
+
stderr_tail: "",
|
|
64
|
+
skipped: true,
|
|
65
|
+
skip_reason: `checker unavailable: ${err.message}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
if (dir)
|
|
70
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function runProcess(cmd, args, timeoutMs) {
|
|
74
|
+
return new Promise((resolve) => {
|
|
75
|
+
const proc = spawn(cmd, args, { shell: process.platform === "win32" });
|
|
76
|
+
let stdout = "";
|
|
77
|
+
let stderr = "";
|
|
78
|
+
const timer = setTimeout(() => proc.kill("SIGKILL"), timeoutMs);
|
|
79
|
+
proc.stdout?.on("data", (d) => (stdout += d.toString()));
|
|
80
|
+
proc.stderr?.on("data", (d) => (stderr += d.toString()));
|
|
81
|
+
proc.on("error", (err) => {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
resolve({ code: -1, stdout, stderr: stderr + String(err) });
|
|
84
|
+
});
|
|
85
|
+
proc.on("close", (code) => {
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
resolve({ code: code ?? -1, stdout, stderr });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function tail(s, n) {
|
|
92
|
+
if (s.length <= n)
|
|
93
|
+
return s;
|
|
94
|
+
return "…" + s.slice(-n);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=compileCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compileCheck.js","sourceRoot":"","sources":["../../src/guardrails/compileCheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,iGAAiG;AACjG,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAY,EACZ,QAA4B;IAE5B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACpH,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAuB,CAAC;IACzD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY;YACf,0EAA0E;YAC1E,yEAAyE;YACzE,wEAAwE;YACxE,4DAA4D;YAC5D,OAAO,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;QACjK,KAAK,YAAY;YACf,OAAO,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACtD,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;QACjE,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;QACvG,KAAK,IAAI;YACP,OAAO,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD;YACE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,yBAAyB,QAAQ,EAAE,EAAE,CAAC;IAClI,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,IAAY,EACZ,GAAW,EACX,OAAe,EACf,QAAkB;IAElB,IAAI,GAAG,GAAkB,IAAI,CAAC;IAC9B,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,EAAE,CAAC,CAAC;QACxC,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,CAAC;QACjC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAC/F,mEAAmE;QACnE,oEAAoE;QACpE,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,OAAO;YACL,QAAQ,EAAE,QAAQ,KAAK,CAAC;YACxB,OAAO,EAAE,GAAG,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE;YAClD,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC;SAClD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,EAAE;YACf,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,wBAAyB,GAAa,CAAC,OAAO,EAAE;SAC9D,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,IAAI,GAAG;YAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,GAAW,EACX,IAAc,EACd,SAAiB;IAEjB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;QACvE,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;QAChE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS;IAChC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5B,OAAO,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confidence guardrail — `ollama_classify` returns a {label, confidence} shape.
|
|
3
|
+
* Below the threshold and with allow_none=true, we return label=null rather
|
|
4
|
+
* than let a weak guess propagate.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_CONFIDENCE_THRESHOLD = 0.7;
|
|
7
|
+
export interface ClassifyRaw {
|
|
8
|
+
label: string | null;
|
|
9
|
+
confidence: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ClassifyGuarded {
|
|
12
|
+
label: string | null;
|
|
13
|
+
confidence: number;
|
|
14
|
+
below_threshold: boolean;
|
|
15
|
+
threshold: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function applyConfidenceThreshold(raw: ClassifyRaw, opts?: {
|
|
18
|
+
threshold?: number;
|
|
19
|
+
allow_none?: boolean;
|
|
20
|
+
}): ClassifyGuarded;
|
|
21
|
+
//# sourceMappingURL=confidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confidence.d.ts","sourceRoot":"","sources":["../../src/guardrails/confidence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,WAAW,EAChB,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,eAAe,CAUjB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confidence guardrail — `ollama_classify` returns a {label, confidence} shape.
|
|
3
|
+
* Below the threshold and with allow_none=true, we return label=null rather
|
|
4
|
+
* than let a weak guess propagate.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_CONFIDENCE_THRESHOLD = 0.7;
|
|
7
|
+
export function applyConfidenceThreshold(raw, opts = {}) {
|
|
8
|
+
const threshold = opts.threshold ?? DEFAULT_CONFIDENCE_THRESHOLD;
|
|
9
|
+
const belowThreshold = raw.confidence < threshold;
|
|
10
|
+
const label = belowThreshold && opts.allow_none ? null : raw.label;
|
|
11
|
+
return {
|
|
12
|
+
label,
|
|
13
|
+
confidence: raw.confidence,
|
|
14
|
+
below_threshold: belowThreshold,
|
|
15
|
+
threshold,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=confidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confidence.js","sourceRoot":"","sources":["../../src/guardrails/confidence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAchD,MAAM,UAAU,wBAAwB,CACtC,GAAgB,EAChB,OAAqD,EAAE;IAEvD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,4BAA4B,CAAC;IACjE,MAAM,cAAc,GAAG,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC;IAClD,MAAM,KAAK,GAAG,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IACnE,OAAO;QACL,KAAK;QACL,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,eAAe,EAAE,cAAc;QAC/B,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout + fallback guardrail.
|
|
3
|
+
*
|
|
4
|
+
* Every tier has a hard timeout (see TIER_TIMEOUT_MS). When the timeout
|
|
5
|
+
* fires, we log the timeout event, decide whether to fall back to a
|
|
6
|
+
* cheaper tier, log the fallback decision, and re-run.
|
|
7
|
+
*
|
|
8
|
+
* Both events land in the NDJSON log. Later this is what proves the
|
|
9
|
+
* system degraded correctly under pressure, not just that a call was slow.
|
|
10
|
+
*/
|
|
11
|
+
import { type Tier } from "../tiers.js";
|
|
12
|
+
import type { Logger } from "../observability.js";
|
|
13
|
+
export interface RunWithTimeoutInput<T> {
|
|
14
|
+
tool: string;
|
|
15
|
+
tier: Tier;
|
|
16
|
+
run: (tier: Tier, signal: AbortSignal) => Promise<T>;
|
|
17
|
+
logger: Logger;
|
|
18
|
+
/** If true, cascade through TIER_FALLBACK on timeout. Default true. */
|
|
19
|
+
allowFallback?: boolean;
|
|
20
|
+
/** Override timeouts per tier. Production leaves this undefined; tests inject short values. */
|
|
21
|
+
timeoutOverrideMs?: Partial<Record<Tier, number>>;
|
|
22
|
+
}
|
|
23
|
+
export interface RunWithTimeoutResult<T> {
|
|
24
|
+
value: T;
|
|
25
|
+
actualTier: Tier;
|
|
26
|
+
fallbackFrom?: Tier;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run `run(tier)` with the tier's timeout. On timeout:
|
|
30
|
+
* 1. log a "timeout" event
|
|
31
|
+
* 2. if TIER_FALLBACK[tier] exists and allowFallback, log a "fallback" event and retry
|
|
32
|
+
* 3. if no fallback or fallback also times out, throw TIER_TIMEOUT
|
|
33
|
+
*/
|
|
34
|
+
export declare function runWithTimeoutAndFallback<T>(input: RunWithTimeoutInput<T>): Promise<RunWithTimeoutResult<T>>;
|
|
35
|
+
//# sourceMappingURL=timeouts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/guardrails/timeouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAkC,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC,KAAK,EAAE,CAAC,CAAC;IACT,UAAU,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAwClC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout + fallback guardrail.
|
|
3
|
+
*
|
|
4
|
+
* Every tier has a hard timeout (see TIER_TIMEOUT_MS). When the timeout
|
|
5
|
+
* fires, we log the timeout event, decide whether to fall back to a
|
|
6
|
+
* cheaper tier, log the fallback decision, and re-run.
|
|
7
|
+
*
|
|
8
|
+
* Both events land in the NDJSON log. Later this is what proves the
|
|
9
|
+
* system degraded correctly under pressure, not just that a call was slow.
|
|
10
|
+
*/
|
|
11
|
+
import { InternError } from "../errors.js";
|
|
12
|
+
import { TIER_FALLBACK, TIER_TIMEOUT_MS } from "../tiers.js";
|
|
13
|
+
import { timestamp } from "../observability.js";
|
|
14
|
+
/**
|
|
15
|
+
* Run `run(tier)` with the tier's timeout. On timeout:
|
|
16
|
+
* 1. log a "timeout" event
|
|
17
|
+
* 2. if TIER_FALLBACK[tier] exists and allowFallback, log a "fallback" event and retry
|
|
18
|
+
* 3. if no fallback or fallback also times out, throw TIER_TIMEOUT
|
|
19
|
+
*/
|
|
20
|
+
export async function runWithTimeoutAndFallback(input) {
|
|
21
|
+
const allowFallback = input.allowFallback ?? true;
|
|
22
|
+
return attempt(input.tier, undefined);
|
|
23
|
+
async function attempt(tier, fallbackFrom) {
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
const timeoutMs = input.timeoutOverrideMs?.[tier] ?? TIER_TIMEOUT_MS[tier];
|
|
26
|
+
let timedOut = false;
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
timedOut = true;
|
|
29
|
+
controller.abort();
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
try {
|
|
32
|
+
const value = await input.run(tier, controller.signal);
|
|
33
|
+
return { value, actualTier: tier, ...(fallbackFrom ? { fallbackFrom } : {}) };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (!timedOut)
|
|
37
|
+
throw err;
|
|
38
|
+
await input.logger.log({ kind: "timeout", ts: timestamp(), tool: input.tool, tier, timeout_ms: timeoutMs });
|
|
39
|
+
const next = allowFallback ? TIER_FALLBACK[tier] : null;
|
|
40
|
+
if (!next) {
|
|
41
|
+
throw new InternError("TIER_TIMEOUT", `Tool ${input.tool} exceeded ${timeoutMs}ms on tier ${tier} with no fallback available`, "Increase the tier's timeout, reduce input size, or ensure the model is resident (check /api/ps).", true);
|
|
42
|
+
}
|
|
43
|
+
await input.logger.log({
|
|
44
|
+
kind: "fallback",
|
|
45
|
+
ts: timestamp(),
|
|
46
|
+
tool: input.tool,
|
|
47
|
+
from: tier,
|
|
48
|
+
to: next,
|
|
49
|
+
reason: `timeout after ${timeoutMs}ms`,
|
|
50
|
+
});
|
|
51
|
+
return attempt(next, fallbackFrom ?? tier);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=timeouts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeouts.js","sourceRoot":"","sources":["../../src/guardrails/timeouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAa,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAmBhD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAA6B;IAE7B,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,IAAI,CAAC;IAClD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAEtC,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,YAA8B;QAC/D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,SAAS,CAAC,CAAC;QACd,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YACvD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAChF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ;gBAAE,MAAM,GAAG,CAAC;YACzB,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;YAC5G,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACxD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,WAAW,CACnB,cAAc,EACd,QAAQ,KAAK,CAAC,IAAI,aAAa,SAAS,cAAc,IAAI,6BAA6B,EACvF,kGAAkG,EAClG,IAAI,CACL,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;gBACrB,IAAI,EAAE,UAAU;gBAChB,EAAE,EAAE,SAAS,EAAE;gBACf,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,IAAI;gBACV,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,iBAAiB,SAAS,IAAI;aACvC,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,IAAI,EAAE,YAAY,IAAI,IAAI,CAAC,CAAC;QAC7C,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-confirm guardrail — refuses drafts targeting protected paths
|
|
3
|
+
* unless the caller explicitly sets confirm_write: true.
|
|
4
|
+
*
|
|
5
|
+
* Uses the versioned list in ../protectedPaths.ts. Never accept a
|
|
6
|
+
* path-list argument here — that would let callers sidestep the policy.
|
|
7
|
+
*/
|
|
8
|
+
export interface WriteConfirmCheckInput {
|
|
9
|
+
target_path: string | undefined;
|
|
10
|
+
confirm_write: boolean | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface WriteConfirmResult {
|
|
13
|
+
/** True when a write was blocked; handler must refuse. */
|
|
14
|
+
blocked: boolean;
|
|
15
|
+
/** Reason for block, for logging. */
|
|
16
|
+
reason?: string;
|
|
17
|
+
/** Matched protected pattern, for logging. */
|
|
18
|
+
pattern?: string;
|
|
19
|
+
/** Version of the protected-path list that evaluated this call. */
|
|
20
|
+
rules_version: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function checkWriteConfirm(input: WriteConfirmCheckInput): WriteConfirmResult;
|
|
23
|
+
/** Throw the structured error to refuse. Handlers call this after logging. */
|
|
24
|
+
export declare function assertWriteAllowed(input: WriteConfirmCheckInput): void;
|
|
25
|
+
//# sourceMappingURL=writeConfirm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeConfirm.d.ts","sourceRoot":"","sources":["../../src/guardrails/writeConfirm.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,EAAE,OAAO,GAAG,SAAS,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,kBAAkB,CAiBnF;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAUtE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-confirm guardrail — refuses drafts targeting protected paths
|
|
3
|
+
* unless the caller explicitly sets confirm_write: true.
|
|
4
|
+
*
|
|
5
|
+
* Uses the versioned list in ../protectedPaths.ts. Never accept a
|
|
6
|
+
* path-list argument here — that would let callers sidestep the policy.
|
|
7
|
+
*/
|
|
8
|
+
import { InternError } from "../errors.js";
|
|
9
|
+
import { matchesProtectedPath, PROTECTED_PATHS_VERSION } from "../protectedPaths.js";
|
|
10
|
+
export function checkWriteConfirm(input) {
|
|
11
|
+
if (!input.target_path) {
|
|
12
|
+
return { blocked: false, rules_version: PROTECTED_PATHS_VERSION };
|
|
13
|
+
}
|
|
14
|
+
const match = matchesProtectedPath(input.target_path);
|
|
15
|
+
if (!match.protected) {
|
|
16
|
+
return { blocked: false, rules_version: PROTECTED_PATHS_VERSION };
|
|
17
|
+
}
|
|
18
|
+
if (input.confirm_write === true) {
|
|
19
|
+
return { blocked: false, rules_version: PROTECTED_PATHS_VERSION };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
blocked: true,
|
|
23
|
+
reason: match.rule?.reason ?? "Protected path",
|
|
24
|
+
pattern: match.rule?.pattern,
|
|
25
|
+
rules_version: PROTECTED_PATHS_VERSION,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Throw the structured error to refuse. Handlers call this after logging. */
|
|
29
|
+
export function assertWriteAllowed(input) {
|
|
30
|
+
const r = checkWriteConfirm(input);
|
|
31
|
+
if (r.blocked) {
|
|
32
|
+
throw new InternError("PROTECTED_PATH_WRITE", `Refusing draft targeting protected path "${input.target_path}" (${r.pattern}): ${r.reason}`, "If you really mean to touch this path, re-call with confirm_write: true and review the diff carefully.", false);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=writeConfirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeConfirm.js","sourceRoot":"","sources":["../../src/guardrails/writeConfirm.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAkBrF,MAAM,UAAU,iBAAiB,CAAC,KAA6B;IAC7D,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;IACpE,CAAC;IACD,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;IACpE,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QACjC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;IACpE,CAAC;IACD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,IAAI,gBAAgB;QAC9C,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO;QAC5B,aAAa,EAAE,uBAAuB;KACvC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB,CAAC,KAA6B;IAC9D,MAAM,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,WAAW,CACnB,sBAAsB,EACtB,4CAA4C,KAAK,CAAC,WAAW,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,EAAE,EAC5F,wGAAwG,EACxG,KAAK,CACN,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ollama Intern MCP — entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* Registers the 8-tool labor surface. Tool descriptions encode *when* to reach
|
|
6
|
+
* for them — Claude picks the tier by picking the tool, and the tool implies
|
|
7
|
+
* the model. ollama_chat is visibly last-resort.
|
|
8
|
+
*/
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import type { RunContext } from "./runContext.js";
|
|
11
|
+
export declare function createServer(ctx: RunContext): McpServer;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAoClD,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CA6OvD"}
|