hmem-mcp 2.9.0 → 3.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/dist/cli-log-exchange.d.ts +15 -0
- package/dist/cli-log-exchange.js +118 -0
- package/dist/cli-log-exchange.js.map +1 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/hmem-config.js +2 -0
- package/dist/hmem-config.js.map +1 -1
- package/dist/hmem-store.d.ts +17 -0
- package/dist/hmem-store.js +79 -6
- package/dist/hmem-store.js.map +1 -1
- package/dist/mcp-server.js +60 -0
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/hmem-read/SKILL.md +110 -26
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-log-exchange.ts
|
|
3
|
+
*
|
|
4
|
+
* Called by Claude Code's Stop hook after every agent response.
|
|
5
|
+
* Reads the last user message from the session JSONL transcript,
|
|
6
|
+
* combines it with the agent's response (from stdin hook JSON),
|
|
7
|
+
* and appends both to the currently active O-entry.
|
|
8
|
+
*
|
|
9
|
+
* Usage: echo '{"transcript_path":"...","last_assistant_message":"..."}' | hmem log-exchange
|
|
10
|
+
*
|
|
11
|
+
* Requires env:
|
|
12
|
+
* HMEM_PROJECT_DIR — root directory for .hmem files
|
|
13
|
+
* HMEM_AGENT_ID — agent identifier (optional)
|
|
14
|
+
*/
|
|
15
|
+
export declare function logExchange(): Promise<void>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-log-exchange.ts
|
|
3
|
+
*
|
|
4
|
+
* Called by Claude Code's Stop hook after every agent response.
|
|
5
|
+
* Reads the last user message from the session JSONL transcript,
|
|
6
|
+
* combines it with the agent's response (from stdin hook JSON),
|
|
7
|
+
* and appends both to the currently active O-entry.
|
|
8
|
+
*
|
|
9
|
+
* Usage: echo '{"transcript_path":"...","last_assistant_message":"..."}' | hmem log-exchange
|
|
10
|
+
*
|
|
11
|
+
* Requires env:
|
|
12
|
+
* HMEM_PROJECT_DIR — root directory for .hmem files
|
|
13
|
+
* HMEM_AGENT_ID — agent identifier (optional)
|
|
14
|
+
*/
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { HmemStore, resolveHmemPath } from "./hmem-store.js";
|
|
18
|
+
import { loadHmemConfig } from "./hmem-config.js";
|
|
19
|
+
/** Read the last real user message from a JSONL transcript file.
|
|
20
|
+
* Only reads the last 500KB to avoid loading huge files into memory. */
|
|
21
|
+
function readLastUserMessage(transcriptPath) {
|
|
22
|
+
if (!fs.existsSync(transcriptPath))
|
|
23
|
+
return null;
|
|
24
|
+
const stat = fs.statSync(transcriptPath);
|
|
25
|
+
const TAIL_BYTES = 5 * 1024 * 1024; // 5MB — large tool outputs can push user messages far back
|
|
26
|
+
const start = Math.max(0, stat.size - TAIL_BYTES);
|
|
27
|
+
const fd = fs.openSync(transcriptPath, "r");
|
|
28
|
+
const buf = Buffer.alloc(Math.min(stat.size, TAIL_BYTES));
|
|
29
|
+
fs.readSync(fd, buf, 0, buf.length, start);
|
|
30
|
+
fs.closeSync(fd);
|
|
31
|
+
const content = buf.toString("utf8");
|
|
32
|
+
// If we started mid-file, skip the first (likely partial) line
|
|
33
|
+
const lines = start > 0 ? content.substring(content.indexOf("\n") + 1).split("\n") : content.split("\n");
|
|
34
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
35
|
+
const line = lines[i].trim();
|
|
36
|
+
if (!line)
|
|
37
|
+
continue;
|
|
38
|
+
try {
|
|
39
|
+
const entry = JSON.parse(line);
|
|
40
|
+
if (entry.type === "user" &&
|
|
41
|
+
entry.message?.role === "user" &&
|
|
42
|
+
!entry.toolUseResult &&
|
|
43
|
+
!entry.isCompactSummary &&
|
|
44
|
+
!entry.isVisibleInTranscriptOnly) {
|
|
45
|
+
const msg = entry.message.content;
|
|
46
|
+
if (typeof msg === "string")
|
|
47
|
+
return msg;
|
|
48
|
+
if (Array.isArray(msg)) {
|
|
49
|
+
return msg.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/** Auto-extract a short title from text (first line, max 80 chars). */
|
|
60
|
+
function extractTitle(text) {
|
|
61
|
+
const firstLine = text.split("\n")[0].trim().replace(/[<>\[\]]/g, "");
|
|
62
|
+
if (firstLine.length <= 80)
|
|
63
|
+
return firstLine;
|
|
64
|
+
const lastSpace = firstLine.substring(0, 80).lastIndexOf(" ");
|
|
65
|
+
return (lastSpace > 40 ? firstLine.substring(0, lastSpace) : firstLine.substring(0, 80));
|
|
66
|
+
}
|
|
67
|
+
export async function logExchange() {
|
|
68
|
+
// Read hook JSON from stdin synchronously (hook pipes JSON in one shot)
|
|
69
|
+
let input;
|
|
70
|
+
try {
|
|
71
|
+
const data = fs.readFileSync(0, "utf8"); // fd 0 = stdin
|
|
72
|
+
input = JSON.parse(data || "{}");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
// Guards
|
|
78
|
+
if (input.stop_hook_active)
|
|
79
|
+
process.exit(0);
|
|
80
|
+
if (!input.transcript_path || !input.last_assistant_message)
|
|
81
|
+
process.exit(0);
|
|
82
|
+
const userMessage = readLastUserMessage(input.transcript_path);
|
|
83
|
+
if (!userMessage)
|
|
84
|
+
process.exit(0);
|
|
85
|
+
// Skip empty exchanges only
|
|
86
|
+
if (userMessage.length < 2)
|
|
87
|
+
process.exit(0);
|
|
88
|
+
// Open hmem store
|
|
89
|
+
const projectDir = process.env.HMEM_PROJECT_DIR || process.env.COUNCIL_PROJECT_DIR;
|
|
90
|
+
if (!projectDir)
|
|
91
|
+
process.exit(0);
|
|
92
|
+
const agentId = process.env.HMEM_AGENT_ID || process.env.COUNCIL_AGENT_ID || "";
|
|
93
|
+
const templateName = agentId.replace(/_\d+$/, "");
|
|
94
|
+
const hmemPath = resolveHmemPath(projectDir, templateName);
|
|
95
|
+
if (!fs.existsSync(hmemPath))
|
|
96
|
+
process.exit(0);
|
|
97
|
+
const hmemConfig = loadHmemConfig(path.dirname(hmemPath));
|
|
98
|
+
const store = new HmemStore(hmemPath, hmemConfig);
|
|
99
|
+
try {
|
|
100
|
+
// Find or create active O-entry
|
|
101
|
+
const activeOId = store.getActiveO();
|
|
102
|
+
// Flatten multiline text for appendChildren (newlines → " | " to prevent depth parsing)
|
|
103
|
+
const flatUser = userMessage.replace(/\n+/g, " | ").substring(0, 25_000);
|
|
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);
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
console.error(`[hmem log-exchange] ${e}`);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
store.close();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=cli-log-exchange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-log-exchange.js","sourceRoot":"","sources":["../src/cli-log-exchange.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,11 @@ switch (command) {
|
|
|
20
20
|
updateSkills();
|
|
21
21
|
break;
|
|
22
22
|
}
|
|
23
|
+
case "log-exchange": {
|
|
24
|
+
const { logExchange } = await import("./cli-log-exchange.js");
|
|
25
|
+
await logExchange();
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
23
28
|
case "version":
|
|
24
29
|
case "--version":
|
|
25
30
|
case "-v":
|
|
@@ -32,6 +37,7 @@ Usage:
|
|
|
32
37
|
hmem serve Start the MCP server (stdio transport)
|
|
33
38
|
hmem init Interactive installer for AI coding tools
|
|
34
39
|
hmem update-skills Copy/update skill files to detected AI tools
|
|
40
|
+
hmem log-exchange Log a chat exchange to active O-entry (called by Stop hook)
|
|
35
41
|
hmem version Show version
|
|
36
42
|
|
|
37
43
|
Environment variables (for serve):
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,OAAO;QACV,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAChC,MAAM;IAER,KAAK,MAAM,CAAC,CAAC,CAAC;QACZ,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QAClD,MAAM,OAAO,EAAE,CAAC;QAChB,MAAM;IACR,CAAC;IAED,KAAK,eAAe,CAAC,CAAC,CAAC;QACrB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QACvD,YAAY,EAAE,CAAC;QACf,MAAM;IACR,CAAC;IAED,KAAK,SAAS,CAAC;IACf,KAAK,WAAW,CAAC;IACjB,KAAK,IAAI;QACP,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM;IAER;QACE,OAAO,CAAC,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,OAAO;QACV,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAChC,MAAM;IAER,KAAK,MAAM,CAAC,CAAC,CAAC;QACZ,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QAClD,MAAM,OAAO,EAAE,CAAC;QAChB,MAAM;IACR,CAAC;IAED,KAAK,eAAe,CAAC,CAAC,CAAC;QACrB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QACvD,YAAY,EAAE,CAAC;QACf,MAAM;IACR,CAAC;IAED,KAAK,cAAc,CAAC,CAAC,CAAC;QACpB,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAC9D,MAAM,WAAW,EAAE,CAAC;QACpB,MAAM;IACR,CAAC;IAED,KAAK,SAAS,CAAC;IACf,KAAK,WAAW,CAAC;IACjB,KAAK,IAAI;QACP,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM;IAER;QACE,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;6EAiB6D,CAAC,CAAC;QAC3E,MAAM;AACV,CAAC"}
|
package/dist/hmem-config.js
CHANGED
|
@@ -11,6 +11,7 @@ export const DEFAULT_PREFIXES = {
|
|
|
11
11
|
N: "Navigator",
|
|
12
12
|
H: "Human",
|
|
13
13
|
R: "Rule",
|
|
14
|
+
O: "Original",
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
17
|
* Default descriptions for prefix category headers (X0000 entries).
|
|
@@ -28,6 +29,7 @@ export const DEFAULT_PREFIX_DESCRIPTIONS = {
|
|
|
28
29
|
N: "(N)avigation and context notes",
|
|
29
30
|
H: "(H)uman — knowledge about the user",
|
|
30
31
|
R: "(R)ules — user-defined rules and constraints",
|
|
32
|
+
O: "(O)riginal context — raw session history with progressive summarization (auto-generated by flush_context)",
|
|
31
33
|
};
|
|
32
34
|
export const DEFAULT_CONFIG = {
|
|
33
35
|
maxCharsPerLevel: [120, 2_500, 10_000, 25_000, 50_000],
|
package/dist/hmem-config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmem-config.js","sourceRoot":"","sources":["../src/hmem-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAiF7B,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,WAAW;IACd,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,WAAW;IACd,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"hmem-config.js","sourceRoot":"","sources":["../src/hmem-config.ts"],"names":[],"mappings":"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"}
|
package/dist/hmem-store.d.ts
CHANGED
|
@@ -197,6 +197,21 @@ export declare class HmemStore {
|
|
|
197
197
|
* Multiple lines at the same indent depth → siblings (new capability)
|
|
198
198
|
*/
|
|
199
199
|
write(prefix: string, content: string, links?: string[], minRole?: AgentRole, favorite?: boolean, tags?: string[], pinned?: boolean): WriteResult;
|
|
200
|
+
/**
|
|
201
|
+
* Write a linear entry with explicit content at each level (no tree branching).
|
|
202
|
+
* Used by flush_context for O-prefix entries. Each level is a single node forming
|
|
203
|
+
* a straight chain: root → .1 → .1.1 → .1.1.1 → .1.1.1.1
|
|
204
|
+
*
|
|
205
|
+
* Recommended usage: L1 (title) + L2 (paragraph summary) + L5 (raw text).
|
|
206
|
+
* L3/L4 are optional intermediate detail levels.
|
|
207
|
+
*/
|
|
208
|
+
writeLinear(prefix: string, levels: {
|
|
209
|
+
l1: string;
|
|
210
|
+
l2?: string;
|
|
211
|
+
l3?: string;
|
|
212
|
+
l4?: string;
|
|
213
|
+
l5?: string;
|
|
214
|
+
}, tags?: string[], links?: string[]): WriteResult;
|
|
200
215
|
/**
|
|
201
216
|
* Read memories with flexible querying.
|
|
202
217
|
*
|
|
@@ -363,6 +378,8 @@ export declare class HmemStore {
|
|
|
363
378
|
title: string;
|
|
364
379
|
content: string;
|
|
365
380
|
}[];
|
|
381
|
+
/** Get or create the active O-entry (for log-exchange hook). */
|
|
382
|
+
getActiveO(): string;
|
|
366
383
|
bumpAccess(id: string): void;
|
|
367
384
|
private bumpNodeAccess;
|
|
368
385
|
/**
|
package/dist/hmem-store.js
CHANGED
|
@@ -285,6 +285,63 @@ export class HmemStore {
|
|
|
285
285
|
})();
|
|
286
286
|
return { id: rootId, timestamp };
|
|
287
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Write a linear entry with explicit content at each level (no tree branching).
|
|
290
|
+
* Used by flush_context for O-prefix entries. Each level is a single node forming
|
|
291
|
+
* a straight chain: root → .1 → .1.1 → .1.1.1 → .1.1.1.1
|
|
292
|
+
*
|
|
293
|
+
* Recommended usage: L1 (title) + L2 (paragraph summary) + L5 (raw text).
|
|
294
|
+
* L3/L4 are optional intermediate detail levels.
|
|
295
|
+
*/
|
|
296
|
+
writeLinear(prefix, levels, tags, links) {
|
|
297
|
+
this.guardCorrupted();
|
|
298
|
+
prefix = prefix.toUpperCase();
|
|
299
|
+
if (!this.cfg.prefixes[prefix]) {
|
|
300
|
+
throw new Error(`Invalid prefix "${prefix}".`);
|
|
301
|
+
}
|
|
302
|
+
if (!levels.l1 || levels.l1.trim().length === 0) {
|
|
303
|
+
throw new Error("L1 content is required.");
|
|
304
|
+
}
|
|
305
|
+
if (!tags || tags.length === 0) {
|
|
306
|
+
throw new Error("Tags are required for linear entries.");
|
|
307
|
+
}
|
|
308
|
+
const validatedTags = this.validateTags(tags);
|
|
309
|
+
const seq = this.nextSeq(prefix);
|
|
310
|
+
const rootId = `${prefix}${String(seq).padStart(4, "0")}`;
|
|
311
|
+
const timestamp = new Date().toISOString();
|
|
312
|
+
const title = this.autoExtractTitle(levels.l1);
|
|
313
|
+
const insertRoot = this.db.prepare(`
|
|
314
|
+
INSERT INTO memories (id, prefix, seq, created_at, updated_at, title, level_1, links, min_role)
|
|
315
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'worker')
|
|
316
|
+
`);
|
|
317
|
+
const insertNode = this.db.prepare(`
|
|
318
|
+
INSERT INTO memory_nodes (id, parent_id, root_id, depth, seq, title, content, created_at, updated_at)
|
|
319
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
320
|
+
`);
|
|
321
|
+
this.db.transaction(() => {
|
|
322
|
+
insertRoot.run(rootId, prefix, seq, timestamp, timestamp, title, levels.l1.trim(), links ? JSON.stringify(links) : null);
|
|
323
|
+
// Build linear chain: .1 → .1.1 → .1.1.1 → .1.1.1.1
|
|
324
|
+
let parentId = rootId;
|
|
325
|
+
const levelData = [
|
|
326
|
+
{ depth: 2, content: levels.l2 },
|
|
327
|
+
{ depth: 3, content: levels.l3 },
|
|
328
|
+
{ depth: 4, content: levels.l4 },
|
|
329
|
+
{ depth: 5, content: levels.l5 },
|
|
330
|
+
];
|
|
331
|
+
for (const { depth, content } of levelData) {
|
|
332
|
+
if (!content || content.trim().length === 0)
|
|
333
|
+
continue; // skip empty levels
|
|
334
|
+
const nodeId = `${parentId}.1`;
|
|
335
|
+
const nodeTitle = this.autoExtractTitle(content.trim());
|
|
336
|
+
insertNode.run(nodeId, parentId, rootId, depth, 1, nodeTitle, content.trim(), timestamp, timestamp);
|
|
337
|
+
parentId = nodeId;
|
|
338
|
+
}
|
|
339
|
+
// Tags on first child node (or root if no children)
|
|
340
|
+
const firstChildId = levels.l2 ? `${rootId}.1` : rootId;
|
|
341
|
+
this.setTags(firstChildId, validatedTags);
|
|
342
|
+
})();
|
|
343
|
+
return { id: rootId, timestamp };
|
|
344
|
+
}
|
|
288
345
|
/**
|
|
289
346
|
* Read memories with flexible querying.
|
|
290
347
|
*
|
|
@@ -561,8 +618,10 @@ export class HmemStore {
|
|
|
561
618
|
readBulkV2(rows, opts) {
|
|
562
619
|
const v2 = this.cfg.bulkReadV2;
|
|
563
620
|
// Step 0: Filter out irrelevant entries (never shown in bulk reads)
|
|
621
|
+
// O-prefix excluded from unfiltered bulk reads (but shown when explicitly requested via prefix="O")
|
|
622
|
+
const explicitPrefix = !!opts.prefix;
|
|
564
623
|
const irrelevantCount = rows.filter(r => r.irrelevant === 1).length;
|
|
565
|
-
const activeRows = rows.filter(r => r.irrelevant !== 1);
|
|
624
|
+
const activeRows = rows.filter(r => r.irrelevant !== 1 && (explicitPrefix || r.prefix !== "O"));
|
|
566
625
|
// Step 0.5: Detect active-prefixes — prefixes where at least one entry has active=1.
|
|
567
626
|
// Non-active entries in these prefixes are still shown (as compact titles) but don't get expansion slots.
|
|
568
627
|
const activePrefixes = new Set();
|
|
@@ -1268,7 +1327,7 @@ export class HmemStore {
|
|
|
1268
1327
|
* Get statistics about the memory store.
|
|
1269
1328
|
*/
|
|
1270
1329
|
stats() {
|
|
1271
|
-
const total = this.db.prepare("SELECT COUNT(*) as c FROM memories WHERE seq > 0").get().c;
|
|
1330
|
+
const total = this.db.prepare("SELECT COUNT(*) as c FROM memories WHERE seq > 0 AND prefix != 'O'").get().c;
|
|
1272
1331
|
const rows = this.db.prepare("SELECT prefix, COUNT(*) as c FROM memories WHERE seq > 0 GROUP BY prefix").all();
|
|
1273
1332
|
const byPrefix = {};
|
|
1274
1333
|
for (const r of rows)
|
|
@@ -1277,7 +1336,7 @@ export class HmemStore {
|
|
|
1277
1336
|
const memChars = this.db.prepare("SELECT COALESCE(SUM(LENGTH(level_1)),0) as c FROM memories WHERE seq > 0").get().c;
|
|
1278
1337
|
const nodeChars = this.db.prepare("SELECT COALESCE(SUM(LENGTH(content)),0) as c FROM memory_nodes").get().c;
|
|
1279
1338
|
// Stale: not accessed in 60+ days (non-obsolete, non-irrelevant)
|
|
1280
|
-
const staleCount = this.db.prepare("SELECT COUNT(*) as c FROM memories WHERE seq > 0 AND irrelevant != 1 AND obsolete != 1 AND last_accessed < datetime('now', '-60 days')").get().c;
|
|
1339
|
+
const staleCount = this.db.prepare("SELECT COUNT(*) as c FROM memories WHERE seq > 0 AND prefix != 'O' AND irrelevant != 1 AND obsolete != 1 AND last_accessed < datetime('now', '-60 days')").get().c;
|
|
1281
1340
|
return { total, byPrefix, totalChars: memChars + nodeChars, staleCount };
|
|
1282
1341
|
}
|
|
1283
1342
|
/**
|
|
@@ -1939,6 +1998,18 @@ export class HmemStore {
|
|
|
1939
1998
|
"WHERE mn.created_at > ? AND m.obsolete != 1 AND m.irrelevant != 1 AND mn.irrelevant != 1 " +
|
|
1940
1999
|
"ORDER BY mn.created_at DESC LIMIT ?").all(since, limit);
|
|
1941
2000
|
}
|
|
2001
|
+
/** Get or create the active O-entry (for log-exchange hook). */
|
|
2002
|
+
getActiveO() {
|
|
2003
|
+
const row = this.db.prepare("SELECT id FROM memories WHERE prefix = 'O' AND active = 1 AND obsolete != 1 AND irrelevant != 1 LIMIT 1").get();
|
|
2004
|
+
if (row)
|
|
2005
|
+
return row.id;
|
|
2006
|
+
// Create new O-entry for today's session
|
|
2007
|
+
const today = new Date().toISOString().substring(0, 10);
|
|
2008
|
+
const result = this.writeLinear("O", { l1: `Session ${today}` }, ["#session"]);
|
|
2009
|
+
this.db.prepare("UPDATE memories SET active = 1, updated_at = ? WHERE id = ?")
|
|
2010
|
+
.run(new Date().toISOString(), result.id);
|
|
2011
|
+
return result.id;
|
|
2012
|
+
}
|
|
1942
2013
|
bumpAccess(id) {
|
|
1943
2014
|
this.db.prepare("UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?").run(new Date().toISOString(), id);
|
|
1944
2015
|
}
|
|
@@ -2363,9 +2434,11 @@ export class HmemStore {
|
|
|
2363
2434
|
if (seen.has(rootId))
|
|
2364
2435
|
continue;
|
|
2365
2436
|
seen.add(rootId);
|
|
2366
|
-
const rootRow = this.db.prepare("SELECT title, level_1, created_at, irrelevant, obsolete FROM memories WHERE id = ?").get(rootId);
|
|
2437
|
+
const rootRow = this.db.prepare("SELECT prefix, title, level_1, created_at, irrelevant, obsolete FROM memories WHERE id = ?").get(rootId);
|
|
2367
2438
|
if (!rootRow || rootRow.irrelevant === 1 || rootRow.obsolete === 1)
|
|
2368
2439
|
continue;
|
|
2440
|
+
if (rootRow.prefix === "O")
|
|
2441
|
+
continue; // O-entries excluded from related discovery
|
|
2369
2442
|
results.push({
|
|
2370
2443
|
id: rootId,
|
|
2371
2444
|
title: rootRow.title || this.autoExtractTitle(rootRow.level_1),
|
|
@@ -2413,7 +2486,7 @@ export class HmemStore {
|
|
|
2413
2486
|
if (ftsRootIds.size === 0)
|
|
2414
2487
|
return [];
|
|
2415
2488
|
const idPlaceholders = [...ftsRootIds].map(() => "?").join(", ");
|
|
2416
|
-
const rows = this.db.prepare(`SELECT id, title, level_1, created_at FROM memories WHERE id IN (${idPlaceholders}) AND seq > 0 AND irrelevant != 1 AND obsolete != 1 LIMIT ?`).all(...ftsRootIds, limit);
|
|
2489
|
+
const rows = this.db.prepare(`SELECT id, title, level_1, created_at FROM memories WHERE id IN (${idPlaceholders}) AND seq > 0 AND prefix != 'O' AND irrelevant != 1 AND obsolete != 1 LIMIT ?`).all(...ftsRootIds, limit);
|
|
2417
2490
|
return rows.map((r) => ({
|
|
2418
2491
|
id: r.id,
|
|
2419
2492
|
title: r.title || this.autoExtractTitle(r.level_1),
|
|
@@ -2520,7 +2593,7 @@ export class HmemStore {
|
|
|
2520
2593
|
// 8. Fetch full entries, filter obsolete + irrelevant
|
|
2521
2594
|
const tagRelated = [];
|
|
2522
2595
|
for (const s of topScored) {
|
|
2523
|
-
const row = this.db.prepare("SELECT * FROM memories WHERE id = ? AND obsolete != 1 AND irrelevant != 1").get(s.id);
|
|
2596
|
+
const row = this.db.prepare("SELECT * FROM memories WHERE id = ? AND prefix != 'O' AND obsolete != 1 AND irrelevant != 1").get(s.id);
|
|
2524
2597
|
if (!row)
|
|
2525
2598
|
continue;
|
|
2526
2599
|
const children = this.fetchChildren(row.id);
|