hmem-mcp 3.0.0 → 3.1.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/cli-log-exchange.js +2 -8
- package/dist/cli-log-exchange.js.map +1 -1
- package/dist/hmem-store.d.ts +31 -1
- package/dist/hmem-store.js +193 -13
- package/dist/hmem-store.js.map +1 -1
- package/dist/mcp-server.js +33 -1
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/dist/cli-log-exchange.js
CHANGED
|
@@ -99,14 +99,8 @@ export async function logExchange() {
|
|
|
99
99
|
try {
|
|
100
100
|
// Find or create active O-entry
|
|
101
101
|
const activeOId = store.getActiveO();
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
const flatAgent = input.last_assistant_message.replace(/\n+/g, " | ").substring(0, 50_000);
|
|
105
|
-
const title = extractTitle(userMessage);
|
|
106
|
-
// appendChildren format: 0 tabs = L2 (direct child of root O-entry)
|
|
107
|
-
// 1 tab = L3, 2 tabs = L4 (user msg), 3 tabs = L5 (agent response)
|
|
108
|
-
const chunk = `${title}\n\t\t${flatUser}\n\t\t\t${flatAgent}`;
|
|
109
|
-
store.appendChildren(activeOId, chunk);
|
|
102
|
+
// appendExchange stores raw text without newline parsing
|
|
103
|
+
store.appendExchange(activeOId, userMessage, input.last_assistant_message);
|
|
110
104
|
}
|
|
111
105
|
catch (e) {
|
|
112
106
|
console.error(`[hmem log-exchange] ${e}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-log-exchange.js","sourceRoot":"","sources":["../src/cli-log-exchange.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cli-log-exchange.js","sourceRoot":"","sources":["../src/cli-log-exchange.ts"],"names":[],"mappings":"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"}
|
package/dist/hmem-store.d.ts
CHANGED
|
@@ -279,7 +279,7 @@ export declare class HmemStore {
|
|
|
279
279
|
* For sub-nodes: updates node content only.
|
|
280
280
|
* Does NOT modify children — use appendChildren to extend the tree.
|
|
281
281
|
*/
|
|
282
|
-
updateNode(id: string, newContent
|
|
282
|
+
updateNode(id: string, newContent?: string, links?: string[], obsolete?: boolean, favorite?: boolean, curatorBypass?: boolean, irrelevant?: boolean, tags?: string[], pinned?: boolean, active?: boolean): boolean;
|
|
283
283
|
/**
|
|
284
284
|
* Append new child nodes under an existing entry (root or node).
|
|
285
285
|
* Content is tab-indented relative to the parent:
|
|
@@ -292,6 +292,16 @@ export declare class HmemStore {
|
|
|
292
292
|
count: number;
|
|
293
293
|
ids: string[];
|
|
294
294
|
};
|
|
295
|
+
/**
|
|
296
|
+
* Append a chat exchange (user prompt + agent response) to an O-entry.
|
|
297
|
+
* Inserts 3 nodes as a linear chain WITHOUT content parsing — newlines are preserved.
|
|
298
|
+
* L2: title (auto-extracted from userText)
|
|
299
|
+
* L4: user message (raw, newlines intact)
|
|
300
|
+
* L5: agent response (raw, newlines intact)
|
|
301
|
+
*/
|
|
302
|
+
appendExchange(parentId: string, userText: string, agentText: string): {
|
|
303
|
+
id: string;
|
|
304
|
+
};
|
|
295
305
|
/**
|
|
296
306
|
* Bump access_count on a root entry or node.
|
|
297
307
|
* Returns true if the entry was found and bumped.
|
|
@@ -570,3 +580,23 @@ export declare function openAgentMemory(projectDir: string, templateName: string
|
|
|
570
580
|
* Open (or create) the shared company knowledge store (company.hmem).
|
|
571
581
|
*/
|
|
572
582
|
export declare function openCompanyMemory(projectDir: string, config?: HmemConfig): HmemStore;
|
|
583
|
+
export interface AgentRouteResult {
|
|
584
|
+
agent: string;
|
|
585
|
+
score: number;
|
|
586
|
+
entryCount: number;
|
|
587
|
+
topEntries: {
|
|
588
|
+
id: string;
|
|
589
|
+
title: string;
|
|
590
|
+
score: number;
|
|
591
|
+
}[];
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Route a task to the best-matching agent based on memory content.
|
|
595
|
+
* Scans all agent .hmem files in the project directory and scores them
|
|
596
|
+
* against the provided tags and/or search keywords.
|
|
597
|
+
*
|
|
598
|
+
* Scoring: for each agent store, find entries sharing the given tags
|
|
599
|
+
* with tier-weighted scoring (rare=3, medium=2, common=1).
|
|
600
|
+
* FTS5 keyword matching supplements tag scoring.
|
|
601
|
+
*/
|
|
602
|
+
export declare function routeTask(projectDir: string, tags: string[], keywords?: string, limit?: number, config?: HmemConfig): AgentRouteResult[];
|
package/dist/hmem-store.js
CHANGED
|
@@ -1387,19 +1387,23 @@ export class HmemStore {
|
|
|
1387
1387
|
*/
|
|
1388
1388
|
updateNode(id, newContent, links, obsolete, favorite, curatorBypass, irrelevant, tags, pinned, active) {
|
|
1389
1389
|
this.guardCorrupted();
|
|
1390
|
-
const trimmed = newContent
|
|
1390
|
+
const trimmed = newContent?.trim();
|
|
1391
1391
|
if (id.includes(".")) {
|
|
1392
1392
|
// Sub-node in memory_nodes — check char limit for its depth
|
|
1393
1393
|
const nodeRow = this.db.prepare("SELECT depth, content FROM memory_nodes WHERE id = ?").get(id);
|
|
1394
1394
|
if (!nodeRow)
|
|
1395
1395
|
return false;
|
|
1396
1396
|
const oldContent = nodeRow.content;
|
|
1397
|
-
const
|
|
1398
|
-
|
|
1399
|
-
|
|
1397
|
+
const sets = [];
|
|
1398
|
+
const params = [];
|
|
1399
|
+
if (trimmed) {
|
|
1400
|
+
const nodeLimit = this.cfg.maxCharsPerLevel[Math.min(nodeRow.depth - 1, this.cfg.maxCharsPerLevel.length - 1)];
|
|
1401
|
+
if (trimmed.length > nodeLimit * HmemStore.CHAR_LIMIT_TOLERANCE) {
|
|
1402
|
+
throw new Error(`Content exceeds ${nodeLimit} character limit (${trimmed.length} chars) for L${nodeRow.depth}.`);
|
|
1403
|
+
}
|
|
1404
|
+
sets.push("content = ?", "title = ?");
|
|
1405
|
+
params.push(trimmed, this.autoExtractTitle(trimmed));
|
|
1400
1406
|
}
|
|
1401
|
-
const sets = ["content = ?", "title = ?"];
|
|
1402
|
-
const params = [trimmed, this.autoExtractTitle(trimmed)];
|
|
1403
1407
|
if (favorite !== undefined) {
|
|
1404
1408
|
sets.push("favorite = ?");
|
|
1405
1409
|
params.push(favorite ? 1 : 0);
|
|
@@ -1440,14 +1444,17 @@ export class HmemStore {
|
|
|
1440
1444
|
return result.changes > 0;
|
|
1441
1445
|
}
|
|
1442
1446
|
else {
|
|
1443
|
-
// Root entry in memories
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
+
// Root entry in memories
|
|
1448
|
+
if (trimmed) {
|
|
1449
|
+
const l1Limit = this.cfg.maxCharsPerLevel[0];
|
|
1450
|
+
if (trimmed.length > l1Limit * HmemStore.CHAR_LIMIT_TOLERANCE) {
|
|
1451
|
+
throw new Error(`Level 1 exceeds ${l1Limit} character limit (${trimmed.length} chars). Keep L1 compact.`);
|
|
1452
|
+
}
|
|
1447
1453
|
}
|
|
1448
1454
|
// Obsolete enforcement: require [✓ID] correction reference
|
|
1449
1455
|
if (obsolete === true && !curatorBypass) {
|
|
1450
|
-
const
|
|
1456
|
+
const contentToCheck = trimmed ?? this.db.prepare("SELECT level_1 FROM memories WHERE id = ?").get(id)?.level_1 ?? "";
|
|
1457
|
+
const correctionMatch = contentToCheck.match(/\[✓([A-Z]\d{4}(?:\.\d+)*)\]/);
|
|
1451
1458
|
if (!correctionMatch) {
|
|
1452
1459
|
throw new Error("Cannot mark as obsolete without [✓ID] correction reference — write the correction first.");
|
|
1453
1460
|
}
|
|
@@ -1477,8 +1484,12 @@ export class HmemStore {
|
|
|
1477
1484
|
}
|
|
1478
1485
|
}
|
|
1479
1486
|
}
|
|
1480
|
-
const sets = [
|
|
1481
|
-
const params = [
|
|
1487
|
+
const sets = [];
|
|
1488
|
+
const params = [];
|
|
1489
|
+
if (trimmed) {
|
|
1490
|
+
sets.push("level_1 = ?", "title = ?");
|
|
1491
|
+
params.push(trimmed, this.autoExtractTitle(trimmed));
|
|
1492
|
+
}
|
|
1482
1493
|
if (links !== undefined) {
|
|
1483
1494
|
sets.push("links = ?");
|
|
1484
1495
|
params.push(links.length > 0 ? JSON.stringify(links) : null);
|
|
@@ -1590,6 +1601,36 @@ export class HmemStore {
|
|
|
1590
1601
|
}
|
|
1591
1602
|
return { count: nodes.length, ids: topLevelIds };
|
|
1592
1603
|
}
|
|
1604
|
+
/**
|
|
1605
|
+
* Append a chat exchange (user prompt + agent response) to an O-entry.
|
|
1606
|
+
* Inserts 3 nodes as a linear chain WITHOUT content parsing — newlines are preserved.
|
|
1607
|
+
* L2: title (auto-extracted from userText)
|
|
1608
|
+
* L4: user message (raw, newlines intact)
|
|
1609
|
+
* L5: agent response (raw, newlines intact)
|
|
1610
|
+
*/
|
|
1611
|
+
appendExchange(parentId, userText, agentText) {
|
|
1612
|
+
this.guardCorrupted();
|
|
1613
|
+
const parentIsRoot = !parentId.includes(".");
|
|
1614
|
+
const rootId = parentIsRoot ? parentId : parentId.split(".")[0];
|
|
1615
|
+
const timestamp = new Date().toISOString();
|
|
1616
|
+
// Find next available seq
|
|
1617
|
+
const maxSeq = parentIsRoot
|
|
1618
|
+
? this.db.prepare("SELECT MAX(seq) as m FROM memory_nodes WHERE parent_id = ? AND depth = 2").get(parentId)?.m ?? 0
|
|
1619
|
+
: this.db.prepare("SELECT MAX(seq) as m FROM memory_nodes WHERE parent_id = ?").get(parentId)?.m ?? 0;
|
|
1620
|
+
const seq = maxSeq + 1;
|
|
1621
|
+
const title = this.autoExtractTitle(userText.split("\n")[0].replace(/[<>\[\]]/g, ""));
|
|
1622
|
+
const l2Id = `${parentId}.${seq}`;
|
|
1623
|
+
const l4Id = `${l2Id}.1`;
|
|
1624
|
+
const l5Id = `${l4Id}.1`;
|
|
1625
|
+
const insertNode = this.db.prepare("INSERT INTO memory_nodes (id, parent_id, root_id, depth, seq, title, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1626
|
+
this.db.transaction(() => {
|
|
1627
|
+
insertNode.run(l2Id, parentId, rootId, 2, seq, title, title, timestamp, timestamp);
|
|
1628
|
+
insertNode.run(l4Id, l2Id, rootId, 4, 1, this.autoExtractTitle(userText), userText, timestamp, timestamp);
|
|
1629
|
+
insertNode.run(l5Id, l4Id, rootId, 5, 1, this.autoExtractTitle(agentText), agentText, timestamp, timestamp);
|
|
1630
|
+
this.db.prepare("UPDATE memories SET updated_at = ? WHERE id = ?").run(timestamp, rootId);
|
|
1631
|
+
})();
|
|
1632
|
+
return { id: l2Id };
|
|
1633
|
+
}
|
|
1593
1634
|
/**
|
|
1594
1635
|
* Bump access_count on a root entry or node.
|
|
1595
1636
|
* Returns true if the entry was found and bumped.
|
|
@@ -2952,4 +2993,143 @@ export function openCompanyMemory(projectDir, config) {
|
|
|
2952
2993
|
const hmemPath = path.join(projectDir, "company.hmem");
|
|
2953
2994
|
return new HmemStore(hmemPath, config);
|
|
2954
2995
|
}
|
|
2996
|
+
/**
|
|
2997
|
+
* Route a task to the best-matching agent based on memory content.
|
|
2998
|
+
* Scans all agent .hmem files in the project directory and scores them
|
|
2999
|
+
* against the provided tags and/or search keywords.
|
|
3000
|
+
*
|
|
3001
|
+
* Scoring: for each agent store, find entries sharing the given tags
|
|
3002
|
+
* with tier-weighted scoring (rare=3, medium=2, common=1).
|
|
3003
|
+
* FTS5 keyword matching supplements tag scoring.
|
|
3004
|
+
*/
|
|
3005
|
+
export function routeTask(projectDir, tags, keywords, limit = 5, config) {
|
|
3006
|
+
// Discover all agent .hmem files — requires multi-agent setup
|
|
3007
|
+
const agentsDir = path.join(projectDir, "Agents");
|
|
3008
|
+
if (!fs.existsSync(agentsDir))
|
|
3009
|
+
return [];
|
|
3010
|
+
const agentDirs = fs.readdirSync(agentsDir).filter(name => {
|
|
3011
|
+
const agentPath = path.join(agentsDir, name);
|
|
3012
|
+
return fs.statSync(agentPath).isDirectory() &&
|
|
3013
|
+
fs.existsSync(path.join(agentPath, `${name}.hmem`));
|
|
3014
|
+
});
|
|
3015
|
+
const results = [];
|
|
3016
|
+
for (const agentName of agentDirs) {
|
|
3017
|
+
const hmemPath = path.join(agentsDir, agentName, `${agentName}.hmem`);
|
|
3018
|
+
let db;
|
|
3019
|
+
try {
|
|
3020
|
+
db = new Database(hmemPath, { readonly: true });
|
|
3021
|
+
}
|
|
3022
|
+
catch {
|
|
3023
|
+
continue;
|
|
3024
|
+
}
|
|
3025
|
+
try {
|
|
3026
|
+
let agentScore = 0;
|
|
3027
|
+
const topEntries = [];
|
|
3028
|
+
// Phase 1: Tag-based scoring
|
|
3029
|
+
if (tags.length > 0) {
|
|
3030
|
+
// Get global tag frequencies for THIS store
|
|
3031
|
+
const freqRows = db.prepare(`
|
|
3032
|
+
SELECT tag, COUNT(DISTINCT
|
|
3033
|
+
CASE WHEN entry_id LIKE '%.%'
|
|
3034
|
+
THEN SUBSTR(entry_id, 1, INSTR(entry_id, '.') - 1)
|
|
3035
|
+
ELSE entry_id END
|
|
3036
|
+
) as freq FROM memory_tags GROUP BY tag
|
|
3037
|
+
`).all();
|
|
3038
|
+
const tagFreq = new Map();
|
|
3039
|
+
for (const r of freqRows)
|
|
3040
|
+
tagFreq.set(r.tag, r.freq);
|
|
3041
|
+
// Find entries sharing any of the given tags
|
|
3042
|
+
const normalizedTags = tags.map(t => t.startsWith("#") ? t.toLowerCase() : `#${t.toLowerCase()}`);
|
|
3043
|
+
const placeholders = normalizedTags.map(() => "?").join(", ");
|
|
3044
|
+
const matchRows = db.prepare(`
|
|
3045
|
+
SELECT
|
|
3046
|
+
CASE WHEN entry_id LIKE '%.%'
|
|
3047
|
+
THEN SUBSTR(entry_id, 1, INSTR(entry_id, '.') - 1)
|
|
3048
|
+
ELSE entry_id END as root_id,
|
|
3049
|
+
tag
|
|
3050
|
+
FROM memory_tags WHERE tag IN (${placeholders})
|
|
3051
|
+
`).all(...normalizedTags);
|
|
3052
|
+
// Group by root and score
|
|
3053
|
+
const byRoot = new Map();
|
|
3054
|
+
for (const r of matchRows) {
|
|
3055
|
+
if (r.root_id.startsWith("O"))
|
|
3056
|
+
continue; // skip O-entries
|
|
3057
|
+
let set = byRoot.get(r.root_id);
|
|
3058
|
+
if (!set) {
|
|
3059
|
+
set = new Set();
|
|
3060
|
+
byRoot.set(r.root_id, set);
|
|
3061
|
+
}
|
|
3062
|
+
set.add(r.tag);
|
|
3063
|
+
}
|
|
3064
|
+
for (const [rootId, matchedTags] of byRoot) {
|
|
3065
|
+
let entryScore = 0;
|
|
3066
|
+
for (const tag of matchedTags) {
|
|
3067
|
+
const freq = tagFreq.get(tag) ?? 999;
|
|
3068
|
+
if (freq <= 5)
|
|
3069
|
+
entryScore += 3;
|
|
3070
|
+
else if (freq <= 20)
|
|
3071
|
+
entryScore += 2;
|
|
3072
|
+
else
|
|
3073
|
+
entryScore += 1;
|
|
3074
|
+
}
|
|
3075
|
+
agentScore += entryScore;
|
|
3076
|
+
// Get entry title
|
|
3077
|
+
const row = db.prepare("SELECT title, level_1 FROM memories WHERE id = ? AND obsolete != 1 AND irrelevant != 1").get(rootId);
|
|
3078
|
+
if (row) {
|
|
3079
|
+
topEntries.push({
|
|
3080
|
+
id: rootId,
|
|
3081
|
+
title: row.title || row.level_1?.substring(0, 50) || rootId,
|
|
3082
|
+
score: entryScore,
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
// Phase 2: FTS5 keyword supplement
|
|
3088
|
+
if (keywords && keywords.trim().length > 0) {
|
|
3089
|
+
try {
|
|
3090
|
+
const words = keywords.trim().split(/\s+/).filter(w => w.length > 3).slice(0, 5);
|
|
3091
|
+
if (words.length > 0) {
|
|
3092
|
+
const orQuery = words.join(" OR ");
|
|
3093
|
+
const ftsRows = db.prepare(`
|
|
3094
|
+
SELECT rm.root_id, rm.node_id FROM hmem_fts_rowid_map rm
|
|
3095
|
+
JOIN hmem_fts f ON f.rowid = rm.fts_rowid
|
|
3096
|
+
WHERE hmem_fts MATCH ? LIMIT 20
|
|
3097
|
+
`).all(orQuery);
|
|
3098
|
+
const ftsRoots = new Set(ftsRows.map(r => r.root_id).filter(id => !id.startsWith("O")));
|
|
3099
|
+
for (const rootId of ftsRoots) {
|
|
3100
|
+
if (topEntries.some(e => e.id === rootId))
|
|
3101
|
+
continue; // already scored via tags
|
|
3102
|
+
const row = db.prepare("SELECT title, level_1 FROM memories WHERE id = ? AND obsolete != 1 AND irrelevant != 1").get(rootId);
|
|
3103
|
+
if (row) {
|
|
3104
|
+
agentScore += 1; // FTS matches get 1 point each
|
|
3105
|
+
topEntries.push({
|
|
3106
|
+
id: rootId,
|
|
3107
|
+
title: row.title || row.level_1?.substring(0, 50) || rootId,
|
|
3108
|
+
score: 1,
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
catch { /* FTS5 may not exist in all stores */ }
|
|
3115
|
+
}
|
|
3116
|
+
if (agentScore > 0) {
|
|
3117
|
+
// Sort entries by score, keep top 5
|
|
3118
|
+
topEntries.sort((a, b) => b.score - a.score);
|
|
3119
|
+
results.push({
|
|
3120
|
+
agent: agentName,
|
|
3121
|
+
score: agentScore,
|
|
3122
|
+
entryCount: topEntries.length,
|
|
3123
|
+
topEntries: topEntries.slice(0, 5),
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
finally {
|
|
3128
|
+
db.close();
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
// Sort agents by score
|
|
3132
|
+
results.sort((a, b) => b.score - a.score);
|
|
3133
|
+
return results.slice(0, limit);
|
|
3134
|
+
}
|
|
2955
3135
|
//# sourceMappingURL=hmem-store.js.map
|