@melandlabs/memory-store 1.1.8 → 1.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/dist/{config-CnbqSek5.d.ts → config-DfvVZ--0.d.ts} +137 -1
- package/dist/http.d.ts +11 -2
- package/dist/http.js +401 -21
- package/dist/index.d.ts +10 -5
- package/dist/index.js +351 -21
- package/dist/mcp.d.ts +12 -2
- package/dist/mcp.js +456 -23
- package/dist/search/derive.d.ts +93 -0
- package/dist/search/derive.js +145 -0
- package/dist/search/distill.d.ts +60 -0
- package/dist/search/distill.js +61 -0
- package/dist/search/unified-search.d.ts +4 -2
- package/dist/search/unified-search.js +180 -23
- package/dist/server/cli-http.js +401 -21
- package/dist/server/cli-mcp.js +454 -21
- package/dist/server/cli-shared.d.ts +3 -1
- package/dist/storage/postgres-raw-message-factory.d.ts +3 -1
- package/dist/storage/raw-message-store.d.ts +3 -1
- package/dist/storage/sqlite-raw-message-store.d.ts +3 -1
- package/dist/storage/sqlite-vector-index.d.ts +3 -1
- package/dist/{unified-search-Bft57TRH.d.ts → unified-search-A8Q3JHwU.d.ts} +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,6 +9,8 @@ export { closeSQLiteVsaStore, getSQLiteVsaStore } from '@melandlabs/sqlite';
|
|
|
9
9
|
import { applyReflectedConsolidationPlan } from '@melandlabs/memory-consolidation';
|
|
10
10
|
import { superpose, bind, unbind, cleanup, cosineSimilarity } from '@melandlabs/vsa';
|
|
11
11
|
import { createRawMessageMemoryGraphStore } from '@melandlabs/indexeddb';
|
|
12
|
+
import { isDerivedKind } from '@melandlabs/contracts/derived-fact';
|
|
13
|
+
import { isEntityKind } from '@melandlabs/contracts/entity-edge';
|
|
12
14
|
|
|
13
15
|
var __defProp = Object.defineProperty;
|
|
14
16
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -259,6 +261,9 @@ init_chroma_memory_index();
|
|
|
259
261
|
init_raw_message_store();
|
|
260
262
|
|
|
261
263
|
// src/search/utilities.ts
|
|
264
|
+
function deriveLexicalKeywords(query) {
|
|
265
|
+
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
266
|
+
}
|
|
262
267
|
var DEFAULT_LIMIT = 10;
|
|
263
268
|
var DEFAULT_THRESHOLD = 0.7;
|
|
264
269
|
var DEFAULT_SOURCES = ["memory", "insights", "knowledge"];
|
|
@@ -372,6 +377,50 @@ function toMemoryResult(result) {
|
|
|
372
377
|
metadata: result.metadata
|
|
373
378
|
};
|
|
374
379
|
}
|
|
380
|
+
function materializeSignals(lists, hit) {
|
|
381
|
+
const channels = [];
|
|
382
|
+
let semantic;
|
|
383
|
+
let lexical;
|
|
384
|
+
let entity;
|
|
385
|
+
for (const list of lists) {
|
|
386
|
+
const channel = listNameToChannel(list.name);
|
|
387
|
+
if (!channel) continue;
|
|
388
|
+
if (channels.includes(channel)) continue;
|
|
389
|
+
let found;
|
|
390
|
+
for (const candidate of list.hits) {
|
|
391
|
+
if (candidate.type === hit.type && candidate.id === hit.id) {
|
|
392
|
+
found = candidate;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (!found) continue;
|
|
397
|
+
channels.push(channel);
|
|
398
|
+
if (channel === "semantic" && semantic === void 0) {
|
|
399
|
+
semantic = found.similarity;
|
|
400
|
+
} else if (channel === "lexical" && lexical === void 0) {
|
|
401
|
+
lexical = found.similarity;
|
|
402
|
+
} else if (channel === "entity" && entity === void 0) {
|
|
403
|
+
entity = found.similarity;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (channels.length === 0) {
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
const out = { channels };
|
|
410
|
+
if (semantic !== void 0) out.semantic = semantic;
|
|
411
|
+
if (lexical !== void 0) out.lexical = lexical;
|
|
412
|
+
if (entity !== void 0) out.entity = entity;
|
|
413
|
+
if (typeof hit.metadata.rrfScore === "number") {
|
|
414
|
+
out.rrf = hit.metadata.rrfScore;
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
function listNameToChannel(name) {
|
|
419
|
+
if (name === "memory-semantic") return "semantic";
|
|
420
|
+
if (name === "memory-bm25" || name === "memory-lexical") return "lexical";
|
|
421
|
+
if (name === "memory-entity") return "entity";
|
|
422
|
+
return void 0;
|
|
423
|
+
}
|
|
375
424
|
function isRawMemorySemanticResult(result) {
|
|
376
425
|
if (!result || typeof result !== "object") {
|
|
377
426
|
return false;
|
|
@@ -791,8 +840,65 @@ async function resolveSearchScopePeers(input) {
|
|
|
791
840
|
}
|
|
792
841
|
|
|
793
842
|
// src/search/unified-search.ts
|
|
794
|
-
function
|
|
795
|
-
|
|
843
|
+
function deriveEntityKeywords(query) {
|
|
844
|
+
const proper = Array.from(new Set(query.match(/\b[A-Z][a-zA-Z]{2,}\b/g) ?? [])).map((w) => w.toLowerCase());
|
|
845
|
+
const lexical = deriveLexicalKeywords(query);
|
|
846
|
+
const seen = /* @__PURE__ */ new Set();
|
|
847
|
+
const out = [];
|
|
848
|
+
for (const word of [...proper, ...lexical]) {
|
|
849
|
+
if (word.length < 2) continue;
|
|
850
|
+
if (seen.has(word)) continue;
|
|
851
|
+
seen.add(word);
|
|
852
|
+
out.push(word);
|
|
853
|
+
}
|
|
854
|
+
return out.slice(0, 16);
|
|
855
|
+
}
|
|
856
|
+
async function runEntitySearchForQuery(deps, input, limit, logger, peerPeers = []) {
|
|
857
|
+
if (typeof deps.entitySearch !== "function") {
|
|
858
|
+
return [];
|
|
859
|
+
}
|
|
860
|
+
const keywords = deriveEntityKeywords(input.query);
|
|
861
|
+
if (keywords.length === 0) {
|
|
862
|
+
return [];
|
|
863
|
+
}
|
|
864
|
+
try {
|
|
865
|
+
const entitySearch = deps.entitySearch;
|
|
866
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
867
|
+
const matches = (await Promise.all(
|
|
868
|
+
filters.map(
|
|
869
|
+
(botId) => entitySearch({
|
|
870
|
+
userId: input.userId,
|
|
871
|
+
keywords,
|
|
872
|
+
limit: Math.ceil(limit / filters.length),
|
|
873
|
+
botId,
|
|
874
|
+
...peerPeers.length > 0 ? { peers: peerPeers } : {}
|
|
875
|
+
})
|
|
876
|
+
)
|
|
877
|
+
)).flat();
|
|
878
|
+
return matches.filter(
|
|
879
|
+
(m) => typeof m?.messageId === "string" && typeof m?.label === "string" && typeof m?.score === "number"
|
|
880
|
+
).map((m) => ({
|
|
881
|
+
type: "memory",
|
|
882
|
+
id: m.messageId,
|
|
883
|
+
// The host's `entitySearch` returns match ids + labels only,
|
|
884
|
+
// so we surface the entity label as `content`. When the same
|
|
885
|
+
// `messageId` is also returned by the semantic/lexical channel
|
|
886
|
+
// (RRF path), the real message content is preserved via the
|
|
887
|
+
// shared `(type, id)` key; otherwise callers can re-fetch the
|
|
888
|
+
// original message by `id` (set in metadata as `sourceMessageId`).
|
|
889
|
+
content: m.label,
|
|
890
|
+
similarity: m.score,
|
|
891
|
+
metadata: {
|
|
892
|
+
scoring: "entity",
|
|
893
|
+
entityLabel: m.label,
|
|
894
|
+
isEntityProjection: true,
|
|
895
|
+
sourceMessageId: m.messageId
|
|
896
|
+
}
|
|
897
|
+
}));
|
|
898
|
+
} catch (error) {
|
|
899
|
+
logger.warn?.("[memory-store] entity search failed:", error);
|
|
900
|
+
return [];
|
|
901
|
+
}
|
|
796
902
|
}
|
|
797
903
|
function parseDateBoundary(value, role) {
|
|
798
904
|
const trimmed = value.trim();
|
|
@@ -1232,10 +1338,39 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1232
1338
|
});
|
|
1233
1339
|
}
|
|
1234
1340
|
}
|
|
1235
|
-
|
|
1341
|
+
let entity;
|
|
1342
|
+
if (typeof deps.entitySearch === "function") {
|
|
1343
|
+
entity = filterByDateRange(
|
|
1344
|
+
await runEntitySearchForQuery(deps, input, limit, logger, peerPeers),
|
|
1345
|
+
input.dateFrom,
|
|
1346
|
+
input.dateTo
|
|
1347
|
+
);
|
|
1348
|
+
if (entity.length === 0) {
|
|
1349
|
+
entity = void 0;
|
|
1350
|
+
} else if (normalizeUnifiedMemoryMergeStrategy(
|
|
1351
|
+
input.mergeStrategy ?? deps.reasoning?.defaultMergeStrategy ?? "rrf"
|
|
1352
|
+
) !== "rrf") {
|
|
1353
|
+
warnings.push({
|
|
1354
|
+
source: "memory",
|
|
1355
|
+
code: "memory_entity_requires_rrf",
|
|
1356
|
+
message: "Entity channel results are fused via RRF; with mergeStrategy:'similarity' standalone entity hits are suppressed (only the entity channel signal is kept on co-occurring hits). Request mergeStrategy:'rrf' to surface entity matches."
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
1360
|
+
warnings.push({
|
|
1361
|
+
source: "memory",
|
|
1362
|
+
code: "memory_entity_search_not_configured",
|
|
1363
|
+
message: "RRF merge requested but entity search is not configured; falling back to semantic+lexical RRF."
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
const out = {
|
|
1236
1367
|
semantic: filterByDateRange(semantic, input.dateFrom, input.dateTo),
|
|
1237
1368
|
lexical: filterByDateRange(lexical, input.dateFrom, input.dateTo)
|
|
1238
1369
|
};
|
|
1370
|
+
if (entity && entity.length > 0) {
|
|
1371
|
+
out.entity = entity;
|
|
1372
|
+
}
|
|
1373
|
+
return out;
|
|
1239
1374
|
}
|
|
1240
1375
|
async function searchUnifiedMemory(input) {
|
|
1241
1376
|
const query = input.query.trim();
|
|
@@ -1506,32 +1641,56 @@ function createUnifiedSearch(deps = {}) {
|
|
|
1506
1641
|
}
|
|
1507
1642
|
function mergeAcrossSources(input) {
|
|
1508
1643
|
const memorySubs = input.memorySubs ?? { semantic: [], lexical: [] };
|
|
1644
|
+
const lists = buildChannelLists(memorySubs, input.insightHits, input.knowledgeHits);
|
|
1645
|
+
if (input.strategy !== "rrf") {
|
|
1646
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1647
|
+
for (const hit of [
|
|
1648
|
+
...memorySubs.semantic,
|
|
1649
|
+
...memorySubs.lexical,
|
|
1650
|
+
...input.insightHits,
|
|
1651
|
+
...input.knowledgeHits
|
|
1652
|
+
]) {
|
|
1653
|
+
const key = `${hit.type}::${hit.id}`;
|
|
1654
|
+
const existing = seen.get(key);
|
|
1655
|
+
if (!existing || hit.similarity > existing.similarity) {
|
|
1656
|
+
seen.set(key, hit);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
const merged2 = mergeUnifiedMemorySearchResults(Array.from(seen.values()), input.limit);
|
|
1660
|
+
return attachSignals(merged2, lists);
|
|
1661
|
+
}
|
|
1509
1662
|
const all = [
|
|
1510
1663
|
...memorySubs.semantic,
|
|
1511
1664
|
...memorySubs.lexical,
|
|
1665
|
+
...memorySubs.entity ?? [],
|
|
1512
1666
|
...input.insightHits,
|
|
1513
1667
|
...input.knowledgeHits
|
|
1514
1668
|
];
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1669
|
+
const merged = lists.length <= 1 ? mergeUnifiedMemorySearchResults(all, input.limit) : mergeUnifiedMemorySearchResultsRrf(lists, input.limit);
|
|
1670
|
+
return attachSignals(merged, lists);
|
|
1671
|
+
}
|
|
1672
|
+
function buildChannelLists(memorySubs, insightHits, knowledgeHits) {
|
|
1518
1673
|
const lists = [];
|
|
1519
|
-
if (memorySubs.semantic.length > 0) {
|
|
1520
|
-
|
|
1674
|
+
if (memorySubs.semantic.length > 0) lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
1675
|
+
if (memorySubs.lexical.length > 0) lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
1676
|
+
if (memorySubs.entity && memorySubs.entity.length > 0) {
|
|
1677
|
+
lists.push({ name: "memory-entity", hits: memorySubs.entity });
|
|
1521
1678
|
}
|
|
1522
|
-
if (
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
lists.push({ name: "knowledge", hits: input.knowledgeHits });
|
|
1530
|
-
}
|
|
1531
|
-
if (lists.length <= 1) {
|
|
1532
|
-
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
1679
|
+
if (insightHits.length > 0) lists.push({ name: "insights", hits: insightHits });
|
|
1680
|
+
if (knowledgeHits.length > 0) lists.push({ name: "knowledge", hits: knowledgeHits });
|
|
1681
|
+
return lists;
|
|
1682
|
+
}
|
|
1683
|
+
function attachSignals(hits, lists) {
|
|
1684
|
+
if (hits.length === 0 || lists.length === 0) {
|
|
1685
|
+
return hits;
|
|
1533
1686
|
}
|
|
1534
|
-
return
|
|
1687
|
+
return hits.map((hit) => {
|
|
1688
|
+
const signals = materializeSignals(lists, hit);
|
|
1689
|
+
if (!signals) {
|
|
1690
|
+
return hit;
|
|
1691
|
+
}
|
|
1692
|
+
return { ...hit, signals };
|
|
1693
|
+
});
|
|
1535
1694
|
}
|
|
1536
1695
|
var DEFAULT_MAX_FACTS = 1e3;
|
|
1537
1696
|
var LOW_CONFIDENCE_THRESHOLD = 0.1;
|
|
@@ -1712,6 +1871,177 @@ function attachMemoryGraphStore(store, input) {
|
|
|
1712
1871
|
store.graphStore = graphStore;
|
|
1713
1872
|
return store;
|
|
1714
1873
|
}
|
|
1874
|
+
var DEFAULT_CANDIDATE_LIMIT = 50;
|
|
1875
|
+
async function deriveFacts(deps, input, logger = console) {
|
|
1876
|
+
const warnings = [];
|
|
1877
|
+
if (typeof deps.deriver !== "function") {
|
|
1878
|
+
warnings.push({
|
|
1879
|
+
code: "derive_deriver_not_configured",
|
|
1880
|
+
message: "No `deriver` is wired into the unified search deps; returning an empty fact list."
|
|
1881
|
+
});
|
|
1882
|
+
return { facts: [], warnings };
|
|
1883
|
+
}
|
|
1884
|
+
const limit = Math.max(1, Math.min(500, input.candidateLimit ?? DEFAULT_CANDIDATE_LIMIT));
|
|
1885
|
+
if (!input.candidateTexts || input.candidateTexts.length === 0) {
|
|
1886
|
+
if (!input.query) {
|
|
1887
|
+
warnings.push({
|
|
1888
|
+
code: "derive_fallback_query_noise",
|
|
1889
|
+
message: "No `query` supplied and no `candidateTexts`; lexical fallback derives keywords from `userId + botIds`, which is rarely useful. Pass `query` (or `candidateTexts`) for topical derivation."
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
let candidateTexts;
|
|
1894
|
+
if (input.candidateTexts && input.candidateTexts.length > 0) {
|
|
1895
|
+
candidateTexts = input.candidateTexts;
|
|
1896
|
+
} else {
|
|
1897
|
+
candidateTexts = await loadCandidateTexts(deps, input, limit, logger);
|
|
1898
|
+
if (candidateTexts.length === 0) {
|
|
1899
|
+
warnings.push({
|
|
1900
|
+
code: "derive_no_candidates",
|
|
1901
|
+
message: "No candidate fact texts available for derivation"
|
|
1902
|
+
});
|
|
1903
|
+
return { facts: [], warnings };
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
let facts;
|
|
1907
|
+
try {
|
|
1908
|
+
facts = await deps.deriver({
|
|
1909
|
+
userId: input.userId,
|
|
1910
|
+
userScope: {
|
|
1911
|
+
userId: input.userId,
|
|
1912
|
+
botIds: input.botIds,
|
|
1913
|
+
dateFrom: input.dateFrom,
|
|
1914
|
+
dateTo: input.dateTo
|
|
1915
|
+
},
|
|
1916
|
+
recentFactTexts: candidateTexts,
|
|
1917
|
+
...input.window ? { window: input.window } : {}
|
|
1918
|
+
});
|
|
1919
|
+
} catch (error) {
|
|
1920
|
+
logger.warn?.("[memory-store/derive] deriver threw:", error);
|
|
1921
|
+
warnings.push({
|
|
1922
|
+
code: "derive_deriver_failed",
|
|
1923
|
+
message: error.message ?? "deriver failed"
|
|
1924
|
+
});
|
|
1925
|
+
return { facts: [], warnings };
|
|
1926
|
+
}
|
|
1927
|
+
if (!Array.isArray(facts)) {
|
|
1928
|
+
warnings.push({
|
|
1929
|
+
code: "derive_deriver_returned_invalid_shape",
|
|
1930
|
+
message: `deriver returned ${typeof facts}; expected DerivedFact[]`
|
|
1931
|
+
});
|
|
1932
|
+
return { facts: [], warnings };
|
|
1933
|
+
}
|
|
1934
|
+
const normalized = [];
|
|
1935
|
+
const now = Date.now();
|
|
1936
|
+
for (const fact of facts) {
|
|
1937
|
+
if (!fact || typeof fact.text !== "string" || fact.text.length === 0 || typeof fact.kind !== "string" || !isDerivedKind(fact.kind) || !Array.isArray(fact.sources)) {
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
normalized.push({
|
|
1941
|
+
...fact,
|
|
1942
|
+
derivedAt: typeof fact.derivedAt === "number" ? fact.derivedAt : now,
|
|
1943
|
+
sources: fact.sources.filter((s) => typeof s === "string")
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
if (input.persist && normalized.length > 0) {
|
|
1947
|
+
try {
|
|
1948
|
+
await input.persist(normalized);
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
logger.warn?.("[memory-store/derive] persist callback threw:", error);
|
|
1951
|
+
warnings.push({
|
|
1952
|
+
code: "derive_persist_failed",
|
|
1953
|
+
message: error.message ?? "derive persist failed"
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return { facts: normalized, warnings };
|
|
1958
|
+
}
|
|
1959
|
+
async function loadCandidateTexts(deps, input, limit, logger) {
|
|
1960
|
+
if (typeof deps.searchRawMessagesLexical !== "function") {
|
|
1961
|
+
return [];
|
|
1962
|
+
}
|
|
1963
|
+
const queryText = (input.query ?? `${input.userId} ${input.botIds?.join(" ") ?? ""}`).trim();
|
|
1964
|
+
const keywords = deriveLexicalKeywords(queryText);
|
|
1965
|
+
if (keywords.length === 0) {
|
|
1966
|
+
return [];
|
|
1967
|
+
}
|
|
1968
|
+
try {
|
|
1969
|
+
const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
1970
|
+
const factTypes = input.factTypes?.length ? input.factTypes : void 0;
|
|
1971
|
+
const hits = (await Promise.all(
|
|
1972
|
+
filters.map(
|
|
1973
|
+
(botId) => deps.searchRawMessagesLexical?.({
|
|
1974
|
+
userId: input.userId,
|
|
1975
|
+
keywords,
|
|
1976
|
+
limit: Math.ceil(limit / filters.length),
|
|
1977
|
+
botId,
|
|
1978
|
+
...input.peers && input.peers.length > 0 ? { peers: input.peers } : {},
|
|
1979
|
+
...factTypes ? { factTypes } : {}
|
|
1980
|
+
})
|
|
1981
|
+
)
|
|
1982
|
+
)).flat().filter(isRawMemorySemanticResult);
|
|
1983
|
+
return hits.map((h) => toMemoryResult(h).content);
|
|
1984
|
+
} catch (error) {
|
|
1985
|
+
logger.warn?.("[memory-store/derive] lexical candidate fetch failed:", error);
|
|
1986
|
+
return [];
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
async function distillRawMessage(deps, input, logger = console) {
|
|
1990
|
+
const warnings = [];
|
|
1991
|
+
if (typeof deps.entityExtractor !== "function") {
|
|
1992
|
+
warnings.push({
|
|
1993
|
+
code: "distill_extractor_not_configured",
|
|
1994
|
+
message: "No `entityExtractor` is wired into the unified search deps; returning an empty entity list."
|
|
1995
|
+
});
|
|
1996
|
+
return { edges: [], warnings };
|
|
1997
|
+
}
|
|
1998
|
+
let edges;
|
|
1999
|
+
try {
|
|
2000
|
+
edges = await deps.entityExtractor({
|
|
2001
|
+
userId: input.userId,
|
|
2002
|
+
messageId: input.messageId,
|
|
2003
|
+
content: input.content
|
|
2004
|
+
});
|
|
2005
|
+
} catch (error) {
|
|
2006
|
+
logger.warn?.("[memory-store/distill] extractor threw:", error);
|
|
2007
|
+
warnings.push({
|
|
2008
|
+
code: "distill_extractor_failed",
|
|
2009
|
+
message: error.message ?? "entity extractor failed"
|
|
2010
|
+
});
|
|
2011
|
+
return { edges: [], warnings };
|
|
2012
|
+
}
|
|
2013
|
+
if (!Array.isArray(edges)) {
|
|
2014
|
+
warnings.push({
|
|
2015
|
+
code: "distill_extractor_returned_invalid_shape",
|
|
2016
|
+
message: `entityExtractor returned ${typeof edges}; expected EntityEdge[]`
|
|
2017
|
+
});
|
|
2018
|
+
return { edges: [], warnings };
|
|
2019
|
+
}
|
|
2020
|
+
const normalized = [];
|
|
2021
|
+
const now = Date.now();
|
|
2022
|
+
for (const edge of edges) {
|
|
2023
|
+
if (!edge || typeof edge.label !== "string" || edge.label.length === 0 || typeof edge.kind !== "string" || !isEntityKind(edge.kind) || typeof edge.relation !== "string" || typeof edge.sourceMessageId !== "string") {
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
normalized.push({
|
|
2027
|
+
...edge,
|
|
2028
|
+
label: edge.label.trim().toLowerCase(),
|
|
2029
|
+
extractedAt: typeof edge.extractedAt === "number" ? edge.extractedAt : now
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
if (input.persist && normalized.length > 0) {
|
|
2033
|
+
try {
|
|
2034
|
+
await input.persist(normalized);
|
|
2035
|
+
} catch (error) {
|
|
2036
|
+
logger.warn?.("[memory-store/distill] persist callback threw:", error);
|
|
2037
|
+
warnings.push({
|
|
2038
|
+
code: "distill_persist_failed",
|
|
2039
|
+
message: error.message ?? "distill persist failed"
|
|
2040
|
+
});
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
return { edges: normalized, warnings };
|
|
2044
|
+
}
|
|
1715
2045
|
|
|
1716
2046
|
// src/search/query-rewriter.ts
|
|
1717
2047
|
var SYSTEM_PROMPT = `You are a memory retrieval assistant.
|
|
@@ -2236,4 +2566,4 @@ async function createMemoryStore(config = {}) {
|
|
|
2236
2566
|
return store;
|
|
2237
2567
|
}
|
|
2238
2568
|
|
|
2239
|
-
export { applyReflectedPlan, attachMemoryGraphStore, clampUnifiedMemorySearchLimit, clampUnifiedMemorySearchThreshold, clearPostgresFactory, closeRawMessageStore, createIdentityIterativePlanner, createIdentityRewriter, createIterativeRecallPlanner, createMemoryStore, createPeerProfile, createRawMessageStore, createUnifiedSearch, createUserVoiceRewriter, createVsaRecall, getRawMessageManager, getRawMessageStorageBackend, hasPostgresFactory, isRawMemorySemanticResult, isRawMessageStorageAvailable, mergeUnifiedMemorySearchResults, mergeUnifiedMemorySearchResultsRrf, normalizeUnifiedMemoryReasoningStrategy, normalizeUnifiedMemorySearchSources, registerPostgresFactory, resolvePostgresFactory, toKnowledgeResult, toMemoryResult };
|
|
2569
|
+
export { applyReflectedPlan, attachMemoryGraphStore, clampUnifiedMemorySearchLimit, clampUnifiedMemorySearchThreshold, clearPostgresFactory, closeRawMessageStore, createIdentityIterativePlanner, createIdentityRewriter, createIterativeRecallPlanner, createMemoryStore, createPeerProfile, createRawMessageStore, createUnifiedSearch, createUserVoiceRewriter, createVsaRecall, deriveFacts, distillRawMessage, getRawMessageManager, getRawMessageStorageBackend, hasPostgresFactory, isRawMemorySemanticResult, isRawMessageStorageAvailable, listNameToChannel, materializeSignals, mergeUnifiedMemorySearchResults, mergeUnifiedMemorySearchResultsRrf, normalizeUnifiedMemoryReasoningStrategy, normalizeUnifiedMemorySearchSources, registerPostgresFactory, resolvePostgresFactory, toKnowledgeResult, toMemoryResult };
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { M as MemoryStoreConfig } from './config-
|
|
2
|
+
import { M as MemoryStoreConfig } from './config-DfvVZ--0.js';
|
|
3
3
|
import '@melandlabs/memory-consolidation';
|
|
4
4
|
import '@melandlabs/contracts/peer';
|
|
5
5
|
import '@melandlabs/contracts';
|
|
6
|
+
import '@melandlabs/contracts/derived-fact';
|
|
7
|
+
import '@melandlabs/contracts/entity-edge';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* @melandlabs/memory-store/mcp — MCP server entry.
|
|
@@ -12,7 +14,15 @@ import '@melandlabs/contracts';
|
|
|
12
14
|
*
|
|
13
15
|
* Tools registered:
|
|
14
16
|
* - memory.search → SearchOutput (read-only; set
|
|
15
|
-
* `synthesize: true` for LLM synthesis)
|
|
17
|
+
* `synthesize: true` for LLM synthesis).
|
|
18
|
+
* Each hit carries a `signals` field with
|
|
19
|
+
* per-channel scores.
|
|
20
|
+
* - memory.distill → DistillOutput (entity extraction from
|
|
21
|
+
* a single raw message; requires
|
|
22
|
+
* `unified.entityExtractor`)
|
|
23
|
+
* - memory.derive → DeriveOutput (fact derivation over a
|
|
24
|
+
* window of candidate texts; requires
|
|
25
|
+
* `unified.deriver`)
|
|
16
26
|
* - memory.writeRawMessage → { ok: boolean }
|
|
17
27
|
* - memory.getRawMessage → RawMessage | null
|
|
18
28
|
* - memory.consolidate → ApplyConsolidateOutput (agentic write-back)
|