@prom.codes/memory-mcp 0.5.1 → 0.7.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/dist/bin.js +74 -3
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1307,6 +1307,45 @@ function reciprocalRankFusion(lists, options = {}) {
|
|
|
1307
1307
|
return options.limit !== void 0 && options.limit >= 0 ? merged.slice(0, options.limit) : merged;
|
|
1308
1308
|
}
|
|
1309
1309
|
|
|
1310
|
+
// dist/temporal.js
|
|
1311
|
+
var RRF_K = 60;
|
|
1312
|
+
var DEFAULT_TEMPORAL_WEIGHT = 2;
|
|
1313
|
+
var RECENT_RE = /\b(latest|most[\s-]?recent|recently|recent|current|currently|now|nowadays|today|newest|up[\s-]?to[\s-]?date|so far)\b/i;
|
|
1314
|
+
var EARLIEST_RE = /\b(earliest|oldest|first|initial|initially|original|originally|at the (?:very )?(?:start|beginning)|back then)\b/i;
|
|
1315
|
+
function detectTemporalIntent(query) {
|
|
1316
|
+
if (query === "")
|
|
1317
|
+
return null;
|
|
1318
|
+
const recent = RECENT_RE.test(query);
|
|
1319
|
+
const earliest = EARLIEST_RE.test(query);
|
|
1320
|
+
if (recent === earliest)
|
|
1321
|
+
return null;
|
|
1322
|
+
return { direction: recent ? "recent" : "earliest" };
|
|
1323
|
+
}
|
|
1324
|
+
function parseTimestamp(iso) {
|
|
1325
|
+
if (iso === null || iso === void 0 || iso === "")
|
|
1326
|
+
return null;
|
|
1327
|
+
const ms = Date.parse(iso);
|
|
1328
|
+
return Number.isFinite(ms) ? ms : null;
|
|
1329
|
+
}
|
|
1330
|
+
function applyTemporalRanking(hits, intent, getTimestampMs, weight = DEFAULT_TEMPORAL_WEIGHT) {
|
|
1331
|
+
if (hits.length <= 1 || weight <= 0)
|
|
1332
|
+
return [...hits];
|
|
1333
|
+
const stamped = hits.map((hit, index) => ({ hit, index, ts: getTimestampMs(hit) })).filter((x) => x.ts !== null);
|
|
1334
|
+
if (stamped.length <= 1)
|
|
1335
|
+
return [...hits];
|
|
1336
|
+
const byTime = [...stamped].sort((a, b) => intent.direction === "recent" ? b.ts - a.ts : a.ts - b.ts);
|
|
1337
|
+
const tempRank = /* @__PURE__ */ new Map();
|
|
1338
|
+
byTime.forEach((x, rank) => tempRank.set(x.index, rank));
|
|
1339
|
+
const scored = hits.map((hit, index) => {
|
|
1340
|
+
const posScore = 1 / (RRF_K + index);
|
|
1341
|
+
const tr = tempRank.get(index);
|
|
1342
|
+
const tempScore = tr === void 0 ? 0 : 1 / (RRF_K + tr);
|
|
1343
|
+
return { hit, index, score: posScore + weight * tempScore };
|
|
1344
|
+
});
|
|
1345
|
+
scored.sort((a, b) => b.score - a.score || a.index - b.index);
|
|
1346
|
+
return scored.map((s) => s.hit);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1310
1349
|
// dist/types.js
|
|
1311
1350
|
var MEMORY_SCOPES = [
|
|
1312
1351
|
"system",
|
|
@@ -1480,6 +1519,8 @@ var SqliteMemoryBackend = class {
|
|
|
1480
1519
|
embedder;
|
|
1481
1520
|
reranker;
|
|
1482
1521
|
rewriter;
|
|
1522
|
+
temporalEnabled;
|
|
1523
|
+
temporalWeight;
|
|
1483
1524
|
/** Record ids whose vector is missing/stale, awaiting a batched embed. */
|
|
1484
1525
|
pendingEmbed = /* @__PURE__ */ new Set();
|
|
1485
1526
|
closed = false;
|
|
@@ -1496,6 +1537,8 @@ var SqliteMemoryBackend = class {
|
|
|
1496
1537
|
this.embedder = opts.embedder;
|
|
1497
1538
|
this.reranker = opts.reranker;
|
|
1498
1539
|
this.rewriter = opts.rewriter;
|
|
1540
|
+
this.temporalEnabled = opts.temporal?.enabled ?? false;
|
|
1541
|
+
this.temporalWeight = opts.temporal?.weight ?? DEFAULT_TEMPORAL_WEIGHT;
|
|
1499
1542
|
if (this.embedder !== void 0)
|
|
1500
1543
|
this.queueUnembedded();
|
|
1501
1544
|
}
|
|
@@ -1619,7 +1662,20 @@ var SqliteMemoryBackend = class {
|
|
|
1619
1662
|
], { limit: poolLimit }).map((f) => f.payload);
|
|
1620
1663
|
}
|
|
1621
1664
|
const reranked = input.rerank === false ? pool : await this.rerankPool(input.query, pool, finalLimit);
|
|
1622
|
-
|
|
1665
|
+
const temporalOn = input.temporal ?? this.temporalEnabled;
|
|
1666
|
+
const ordered = temporalOn ? this.applyTemporal(input.query, reranked) : reranked;
|
|
1667
|
+
return ordered.slice(0, finalLimit);
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* M5: blend the current order with a timestamp order when the query expresses
|
|
1671
|
+
* a clear temporal direction. Uses `updatedAt` (falls back to `createdAt`) as
|
|
1672
|
+
* the record's time. No-op when no intent is detected.
|
|
1673
|
+
*/
|
|
1674
|
+
applyTemporal(query, hits) {
|
|
1675
|
+
const intent = detectTemporalIntent(query);
|
|
1676
|
+
if (intent === null)
|
|
1677
|
+
return hits;
|
|
1678
|
+
return applyTemporalRanking(hits, intent, (h) => parseTimestamp(h.record.updatedAt) ?? parseTimestamp(h.record.createdAt), this.temporalWeight);
|
|
1623
1679
|
}
|
|
1624
1680
|
/**
|
|
1625
1681
|
* M4: rewrite the query for the retrieval channels (HyDE concat). Returns the
|
|
@@ -2076,6 +2132,18 @@ function discoverMemoryRewriter(env) {
|
|
|
2076
2132
|
}
|
|
2077
2133
|
throw new Error(`unknown PROMETHEUS_MEMORY_REWRITE_PROVIDER="${forced}" (expected "none", "mistral", "openai", or "generic")`);
|
|
2078
2134
|
}
|
|
2135
|
+
function discoverMemoryTemporal(env) {
|
|
2136
|
+
const raw = (env.PROMETHEUS_MEMORY_TEMPORAL ?? "on").toLowerCase();
|
|
2137
|
+
const enabled = !(raw === "off" || raw === "0" || raw === "false" || raw === "no");
|
|
2138
|
+
const rawWeight = env.PROMETHEUS_MEMORY_TEMPORAL_WEIGHT;
|
|
2139
|
+
let weight = DEFAULT_TEMPORAL_WEIGHT;
|
|
2140
|
+
if (rawWeight !== void 0 && rawWeight !== "") {
|
|
2141
|
+
const n = Number.parseFloat(rawWeight);
|
|
2142
|
+
if (Number.isFinite(n) && n >= 0)
|
|
2143
|
+
weight = n;
|
|
2144
|
+
}
|
|
2145
|
+
return { enabled, weight };
|
|
2146
|
+
}
|
|
2079
2147
|
function composeFromEnv(opts) {
|
|
2080
2148
|
const env = opts.env;
|
|
2081
2149
|
const override = (opts.workspaceRootOverride ?? "").trim();
|
|
@@ -2090,10 +2158,12 @@ function composeFromEnv(opts) {
|
|
|
2090
2158
|
const { id: rerankerId, provider: reranker } = discoverMemoryReranker(env);
|
|
2091
2159
|
const { id: extractorId, provider: extractor } = discoverMemoryExtractor(env);
|
|
2092
2160
|
const { id: rewriterId, provider: rewriter } = discoverMemoryRewriter(env);
|
|
2161
|
+
const temporal = discoverMemoryTemporal(env);
|
|
2093
2162
|
const backend = new SqliteMemoryBackend(dbPath, {
|
|
2094
2163
|
...embedder !== void 0 ? { embedder } : {},
|
|
2095
2164
|
...reranker !== null ? { reranker } : {},
|
|
2096
|
-
...rewriter !== null ? { rewriter } : {}
|
|
2165
|
+
...rewriter !== null ? { rewriter } : {},
|
|
2166
|
+
temporal
|
|
2097
2167
|
});
|
|
2098
2168
|
return {
|
|
2099
2169
|
backend,
|
|
@@ -2109,6 +2179,7 @@ function composeFromEnv(opts) {
|
|
|
2109
2179
|
extractorId,
|
|
2110
2180
|
rewriter,
|
|
2111
2181
|
rewriterId,
|
|
2182
|
+
temporalEnabled: temporal.enabled,
|
|
2112
2183
|
close: () => backend.close()
|
|
2113
2184
|
};
|
|
2114
2185
|
}
|
|
@@ -2735,7 +2806,7 @@ async function main() {
|
|
|
2735
2806
|
env,
|
|
2736
2807
|
...override !== void 0 && override !== "" ? { workspaceRootOverride: override } : {}
|
|
2737
2808
|
});
|
|
2738
|
-
process.stderr.write(`prometheus-memory-mcp: workspace=${composed.workspaceRoot} (via ${via}) project=${composed.projectName} (${composed.projectId}) db=${composed.dbPath} embed=${composed.embedderId}${composed.embeddingsEnabled ? "" : " (keyword-only)"} rerank=${composed.rerankerId} extract=${composed.extractorId} rewrite=${composed.rewriterId}
|
|
2809
|
+
process.stderr.write(`prometheus-memory-mcp: workspace=${composed.workspaceRoot} (via ${via}) project=${composed.projectName} (${composed.projectId}) db=${composed.dbPath} embed=${composed.embedderId}${composed.embeddingsEnabled ? "" : " (keyword-only)"} rerank=${composed.rerankerId} extract=${composed.extractorId} rewrite=${composed.rewriterId} temporal=${composed.temporalEnabled ? "on" : "off"}
|
|
2739
2810
|
`);
|
|
2740
2811
|
registerTools(server, composed);
|
|
2741
2812
|
};
|