@cachly-dev/mcp-server 0.9.15 → 0.9.17
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/dist/index.js +110 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +106 -1
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ import { Redis } from 'ioredis';
|
|
|
47
47
|
const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
|
|
48
48
|
let JWT = process.env.CACHLY_JWT ?? '';
|
|
49
49
|
const EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
|
|
50
|
-
const CURRENT_VERSION = '0.9.
|
|
50
|
+
const CURRENT_VERSION = '0.9.17';
|
|
51
51
|
// ── Default Instance Resolution (for Smithery & single-credential setups) ────
|
|
52
52
|
// When CACHLY_BRAIN_INSTANCE_ID is set, tools can omit the instance_id parameter.
|
|
53
53
|
// When neither is set, we auto-fetch the first running instance once per process.
|
|
@@ -5765,6 +5765,115 @@ async function handleTool(name, args) {
|
|
|
5765
5765
|
}
|
|
5766
5766
|
}
|
|
5767
5767
|
catch { /* non-critical */ }
|
|
5768
|
+
// ── 🔮 PPE: Predictive Pre-fetch — CKG-powered risk detection ────────────
|
|
5769
|
+
// Layer 4: Before starting any work, scan the CKG for nodes matching focus
|
|
5770
|
+
// tokens and traverse causal edges to surface predicted failure points inline.
|
|
5771
|
+
// This is the PPE "pre-fetch" that was previously only available via brain_predict.
|
|
5772
|
+
if (focus && focus.length > 3) {
|
|
5773
|
+
try {
|
|
5774
|
+
const ppeFocusTokens = focus.toLowerCase().replace(/[^a-z0-9\s\-_:]/g, ' ').split(/\s+/).filter(t => t.length > 2).slice(0, 5);
|
|
5775
|
+
const ppePredictions = [];
|
|
5776
|
+
for (const token of ppeFocusTokens) {
|
|
5777
|
+
const nodeKeys = [];
|
|
5778
|
+
const nStream = redis.scanStream({ match: `cachly:ckg:node:*${token}*`, count: 20 });
|
|
5779
|
+
await new Promise((res, rej) => { nStream.on('data', (b) => nodeKeys.push(...b)); nStream.on('end', res); nStream.on('error', rej); });
|
|
5780
|
+
for (const nk of nodeKeys.slice(0, 3)) {
|
|
5781
|
+
const nodeRaw = await redis.get(nk);
|
|
5782
|
+
if (!nodeRaw)
|
|
5783
|
+
continue;
|
|
5784
|
+
const node = JSON.parse(nodeRaw);
|
|
5785
|
+
const edgeKeys = await redis.smembers(`cachly:ckg:idx:from:${node.id}`);
|
|
5786
|
+
for (const ek of edgeKeys.slice(0, 15)) {
|
|
5787
|
+
const edgeRaw = await redis.get(ek);
|
|
5788
|
+
if (!edgeRaw)
|
|
5789
|
+
continue;
|
|
5790
|
+
const edge = JSON.parse(edgeRaw);
|
|
5791
|
+
if (edge.edgeType !== 'causes' && edge.edgeType !== 'co-occurs' && edge.edgeType !== 'fixes')
|
|
5792
|
+
continue;
|
|
5793
|
+
if (edge.confidence < 0.35)
|
|
5794
|
+
continue;
|
|
5795
|
+
// For "fixes" edges, the lesson is on the "from" node (fix for "to")
|
|
5796
|
+
// For "causes" edges, target is the failure mode
|
|
5797
|
+
const lessonKey = edge.edgeType === 'fixes' ? `cachly:lesson:best:${edge.from}` : `cachly:lesson:best:${edge.to}`;
|
|
5798
|
+
const lessonRaw = await redis.get(lessonKey);
|
|
5799
|
+
const lesson = lessonRaw ? JSON.parse(lessonRaw) : undefined;
|
|
5800
|
+
ppePredictions.push({ concept: node.id, edgeType: edge.edgeType, target: edge.to, confidence: edge.confidence, lesson });
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
const ppeSeen = new Set();
|
|
5805
|
+
const ppeUniq = ppePredictions
|
|
5806
|
+
.filter(p => { const k = `${p.concept}:${p.target}`; if (ppeSeen.has(k))
|
|
5807
|
+
return false; ppeSeen.add(k); return true; })
|
|
5808
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
5809
|
+
.slice(0, 4);
|
|
5810
|
+
if (ppeUniq.length > 0) {
|
|
5811
|
+
lines.push(`🔮 **Predicted risks for "${focus}"** (PPE pre-fetch):`);
|
|
5812
|
+
for (const p of ppeUniq) {
|
|
5813
|
+
const confPct = Math.round(p.confidence * 100);
|
|
5814
|
+
const icon = p.edgeType === 'causes' ? '⚡' : p.edgeType === 'fixes' ? '🔧' : '🔄';
|
|
5815
|
+
lines.push(` ${icon} **${confPct}%** \`${p.concept}\` ${p.edgeType} \`${p.target}\``);
|
|
5816
|
+
if (p.lesson?.what_worked)
|
|
5817
|
+
lines.push(` ✅ ${p.lesson.what_worked.slice(0, 110)}`);
|
|
5818
|
+
}
|
|
5819
|
+
lines.push(` _Full analysis: \`brain_predict(context="${focus}")\`_`);
|
|
5820
|
+
lines.push('');
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
catch { /* non-critical — never block session start */ }
|
|
5824
|
+
}
|
|
5825
|
+
// ── 📐 MCM Confidence Calibration — 30-day accuracy pass ─────────────────
|
|
5826
|
+
// Layer 7: Periodically check if high-confidence lessons are actually reliable.
|
|
5827
|
+
// If last calibration was >30 days ago (or never), run a quick pass.
|
|
5828
|
+
// Measures: of recalled lessons with confidence > 0.85, what % had outcome=success?
|
|
5829
|
+
try {
|
|
5830
|
+
const calRaw = await redis.get('cachly:mcm:calibration:last');
|
|
5831
|
+
const lastCalMs = calRaw ? JSON.parse(calRaw).ts : 0;
|
|
5832
|
+
const daysSinceCal = (Date.now() - lastCalMs) / 86_400_000;
|
|
5833
|
+
if (daysSinceCal >= 30 && lessons.length >= 5) {
|
|
5834
|
+
// Quick calibration pass on recalled lessons (recall_count > 0, outcome=success)
|
|
5835
|
+
const recalledSuccess = lessons.filter(l => l.outcome === 'success' && (l.recall_count ?? 0) > 0);
|
|
5836
|
+
const recalledAll = lessons.filter(l => (l.recall_count ?? 0) > 0);
|
|
5837
|
+
if (recalledAll.length >= 3) {
|
|
5838
|
+
const precision = recalledAll.length > 0 ? recalledSuccess.length / recalledAll.length : 1;
|
|
5839
|
+
const lowPrecisionDomains = [];
|
|
5840
|
+
// Per-domain breakdown
|
|
5841
|
+
const domCalMap = new Map();
|
|
5842
|
+
for (const l of recalledAll) {
|
|
5843
|
+
const dom = l.topic.split(':')[0] ?? 'other';
|
|
5844
|
+
if (!domCalMap.has(dom))
|
|
5845
|
+
domCalMap.set(dom, { success: 0, total: 0 });
|
|
5846
|
+
const d = domCalMap.get(dom);
|
|
5847
|
+
d.total++;
|
|
5848
|
+
if (l.outcome === 'success')
|
|
5849
|
+
d.success++;
|
|
5850
|
+
}
|
|
5851
|
+
for (const [dom, d] of domCalMap) {
|
|
5852
|
+
if (d.total >= 2 && d.success / d.total < 0.6)
|
|
5853
|
+
lowPrecisionDomains.push(dom);
|
|
5854
|
+
}
|
|
5855
|
+
// Save calibration result
|
|
5856
|
+
await redis.set('cachly:mcm:calibration:last', JSON.stringify({
|
|
5857
|
+
ts: Date.now(),
|
|
5858
|
+
precision: precision,
|
|
5859
|
+
recalled: recalledAll.length,
|
|
5860
|
+
low_precision_domains: lowPrecisionDomains,
|
|
5861
|
+
}));
|
|
5862
|
+
if (lowPrecisionDomains.length > 0 || precision < 0.7) {
|
|
5863
|
+
lines.push(`📐 **MCM Calibration** (30-day pass — ${recalledAll.length} recalled lessons):`);
|
|
5864
|
+
lines.push(` Overall precision: **${(precision * 100).toFixed(0)}%** recalled lessons actually worked`);
|
|
5865
|
+
if (lowPrecisionDomains.length > 0) {
|
|
5866
|
+
lines.push(` ⚠️ Low-precision domains: ${lowPrecisionDomains.map(d => `\`${d}\``).join(', ')} — consider revisiting these lessons`);
|
|
5867
|
+
}
|
|
5868
|
+
lines.push(` 💡 Use \`learn_from_attempts\` to update stale lessons and improve accuracy.`);
|
|
5869
|
+
lines.push('');
|
|
5870
|
+
}
|
|
5871
|
+
}
|
|
5872
|
+
// Even if no issues, stamp the date so we don't re-check for 30d
|
|
5873
|
+
await redis.set('cachly:mcm:calibration:last', JSON.stringify({ ts: Date.now(), precision: 1, recalled: 0 }), 'EX', 35 * 86400);
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
catch { /* non-critical */ }
|
|
5768
5877
|
// ── 🌍 Knowledge Commons — community stats banner ───────────────────────
|
|
5769
5878
|
// Fetch syndication stats and show a 1-liner: total lessons + confirms + weekly growth.
|
|
5770
5879
|
// Non-fatal: never blocks session start if the API call fails.
|