@psiclawops/hypermem 0.5.0 → 0.5.2
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/ARCHITECTURE.md +12 -3
- package/README.md +30 -6
- package/bin/hypermem-status.mjs +166 -0
- package/dist/background-indexer.d.ts +132 -0
- package/dist/background-indexer.d.ts.map +1 -0
- package/dist/background-indexer.js +1044 -0
- package/dist/cache.d.ts +110 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +495 -0
- package/dist/compaction-fence.d.ts +89 -0
- package/dist/compaction-fence.d.ts.map +1 -0
- package/dist/compaction-fence.js +153 -0
- package/dist/compositor.d.ts +226 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +2558 -0
- package/dist/content-type-classifier.d.ts +41 -0
- package/dist/content-type-classifier.d.ts.map +1 -0
- package/dist/content-type-classifier.js +181 -0
- package/dist/cross-agent.d.ts +62 -0
- package/dist/cross-agent.d.ts.map +1 -0
- package/dist/cross-agent.js +259 -0
- package/dist/db.d.ts +131 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +402 -0
- package/dist/desired-state-store.d.ts +100 -0
- package/dist/desired-state-store.d.ts.map +1 -0
- package/dist/desired-state-store.js +222 -0
- package/dist/doc-chunk-store.d.ts +140 -0
- package/dist/doc-chunk-store.d.ts.map +1 -0
- package/dist/doc-chunk-store.js +391 -0
- package/dist/doc-chunker.d.ts +99 -0
- package/dist/doc-chunker.d.ts.map +1 -0
- package/dist/doc-chunker.js +324 -0
- package/dist/dreaming-promoter.d.ts +86 -0
- package/dist/dreaming-promoter.d.ts.map +1 -0
- package/dist/dreaming-promoter.js +381 -0
- package/dist/episode-store.d.ts +49 -0
- package/dist/episode-store.d.ts.map +1 -0
- package/dist/episode-store.js +135 -0
- package/dist/fact-store.d.ts +75 -0
- package/dist/fact-store.d.ts.map +1 -0
- package/dist/fact-store.js +236 -0
- package/dist/fleet-store.d.ts +144 -0
- package/dist/fleet-store.d.ts.map +1 -0
- package/dist/fleet-store.js +276 -0
- package/dist/fos-mod.d.ts +178 -0
- package/dist/fos-mod.d.ts.map +1 -0
- package/dist/fos-mod.js +416 -0
- package/dist/hybrid-retrieval.d.ts +64 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -0
- package/dist/hybrid-retrieval.js +344 -0
- package/dist/image-eviction.d.ts +49 -0
- package/dist/image-eviction.d.ts.map +1 -0
- package/dist/image-eviction.js +251 -0
- package/dist/index.d.ts +650 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1072 -0
- package/dist/keystone-scorer.d.ts +51 -0
- package/dist/keystone-scorer.d.ts.map +1 -0
- package/dist/keystone-scorer.js +52 -0
- package/dist/knowledge-graph.d.ts +110 -0
- package/dist/knowledge-graph.d.ts.map +1 -0
- package/dist/knowledge-graph.js +305 -0
- package/dist/knowledge-lint.d.ts +29 -0
- package/dist/knowledge-lint.d.ts.map +1 -0
- package/dist/knowledge-lint.js +116 -0
- package/dist/knowledge-store.d.ts +72 -0
- package/dist/knowledge-store.d.ts.map +1 -0
- package/dist/knowledge-store.js +247 -0
- package/dist/library-schema.d.ts +22 -0
- package/dist/library-schema.d.ts.map +1 -0
- package/dist/library-schema.js +1038 -0
- package/dist/message-store.d.ts +89 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +323 -0
- package/dist/metrics-dashboard.d.ts +114 -0
- package/dist/metrics-dashboard.d.ts.map +1 -0
- package/dist/metrics-dashboard.js +260 -0
- package/dist/obsidian-exporter.d.ts +57 -0
- package/dist/obsidian-exporter.d.ts.map +1 -0
- package/dist/obsidian-exporter.js +274 -0
- package/dist/obsidian-watcher.d.ts +147 -0
- package/dist/obsidian-watcher.d.ts.map +1 -0
- package/dist/obsidian-watcher.js +403 -0
- package/dist/open-domain.d.ts +46 -0
- package/dist/open-domain.d.ts.map +1 -0
- package/dist/open-domain.js +125 -0
- package/dist/preference-store.d.ts +54 -0
- package/dist/preference-store.d.ts.map +1 -0
- package/dist/preference-store.js +109 -0
- package/dist/preservation-gate.d.ts +82 -0
- package/dist/preservation-gate.d.ts.map +1 -0
- package/dist/preservation-gate.js +150 -0
- package/dist/proactive-pass.d.ts +63 -0
- package/dist/proactive-pass.d.ts.map +1 -0
- package/dist/proactive-pass.js +239 -0
- package/dist/profiles.d.ts +44 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +227 -0
- package/dist/provider-translator.d.ts +50 -0
- package/dist/provider-translator.d.ts.map +1 -0
- package/dist/provider-translator.js +403 -0
- package/dist/rate-limiter.d.ts +76 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +179 -0
- package/dist/repair-tool-pairs.d.ts +38 -0
- package/dist/repair-tool-pairs.d.ts.map +1 -0
- package/dist/repair-tool-pairs.js +138 -0
- package/dist/retrieval-policy.d.ts +51 -0
- package/dist/retrieval-policy.d.ts.map +1 -0
- package/dist/retrieval-policy.js +77 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +229 -0
- package/dist/secret-scanner.d.ts +51 -0
- package/dist/secret-scanner.d.ts.map +1 -0
- package/dist/secret-scanner.js +248 -0
- package/dist/seed.d.ts +108 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +177 -0
- package/dist/session-flusher.d.ts +53 -0
- package/dist/session-flusher.d.ts.map +1 -0
- package/dist/session-flusher.js +69 -0
- package/dist/session-topic-map.d.ts +41 -0
- package/dist/session-topic-map.d.ts.map +1 -0
- package/dist/session-topic-map.js +77 -0
- package/dist/spawn-context.d.ts +54 -0
- package/dist/spawn-context.d.ts.map +1 -0
- package/dist/spawn-context.js +159 -0
- package/dist/system-store.d.ts +73 -0
- package/dist/system-store.d.ts.map +1 -0
- package/dist/system-store.js +182 -0
- package/dist/temporal-store.d.ts +80 -0
- package/dist/temporal-store.d.ts.map +1 -0
- package/dist/temporal-store.js +149 -0
- package/dist/topic-detector.d.ts +35 -0
- package/dist/topic-detector.d.ts.map +1 -0
- package/dist/topic-detector.js +249 -0
- package/dist/topic-store.d.ts +45 -0
- package/dist/topic-store.d.ts.map +1 -0
- package/dist/topic-store.js +136 -0
- package/dist/topic-synthesizer.d.ts +51 -0
- package/dist/topic-synthesizer.d.ts.map +1 -0
- package/dist/topic-synthesizer.js +315 -0
- package/dist/trigger-registry.d.ts +63 -0
- package/dist/trigger-registry.d.ts.map +1 -0
- package/dist/trigger-registry.js +163 -0
- package/dist/types.d.ts +537 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/vector-store.d.ts +170 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +677 -0
- package/dist/version.d.ts +34 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +34 -0
- package/dist/wiki-page-emitter.d.ts +65 -0
- package/dist/wiki-page-emitter.d.ts.map +1 -0
- package/dist/wiki-page-emitter.js +258 -0
- package/dist/work-store.d.ts +112 -0
- package/dist/work-store.d.ts.map +1 -0
- package/dist/work-store.js +273 -0
- package/package.json +4 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -161,7 +161,7 @@ This means:
|
|
|
161
161
|
|
|
162
162
|
## Context Engine Plugin
|
|
163
163
|
|
|
164
|
-
`plugin/src/index.ts` — OpenClaw context engine plugin (
|
|
164
|
+
`plugin/src/index.ts` — OpenClaw context engine plugin (`hypercompositor`, fills `contextEngine` slot):
|
|
165
165
|
|
|
166
166
|
```
|
|
167
167
|
gateway:startup → Init hypermem, auto-rotate DBs, hydrate fleet cache
|
|
@@ -171,7 +171,15 @@ agent:afterTurn → Ingest new messages to SQLite + Redis, trigger backgroun
|
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
Registers with `ownsCompaction: true` — runtime skips legacy compaction entirely.
|
|
174
|
-
|
|
174
|
+
|
|
175
|
+
## Memory Plugin
|
|
176
|
+
|
|
177
|
+
`memory-plugin/src/index.ts` — Lightweight memory provider (`hypermem`, fills `memory` slot):
|
|
178
|
+
|
|
179
|
+
- Registers `MemoryPluginCapability` with a `MemorySearchManager` backed by HyperMem's hybrid FTS5 + KNN retrieval
|
|
180
|
+
- Provides the `memory_search` tool through the official memory slot interface
|
|
181
|
+
- Public artifacts provider lists `MEMORY.md` and `memory/*.md` for all configured agents
|
|
182
|
+
- Stateless wrapper: lifecycle is owned by the context engine plugin
|
|
175
183
|
|
|
176
184
|
### Plugin Data Flow
|
|
177
185
|
|
|
@@ -269,7 +277,8 @@ Incident history: `specs/HYPERMEM_INCIDENT_HISTORY.md`
|
|
|
269
277
|
| `episode-store.ts` | ~180 | L4 | Significant event tracking |
|
|
270
278
|
| `preference-store.ts` | ~170 | L4 | Operator behavioral patterns |
|
|
271
279
|
| `topic-store.ts` | ~160 | L4 | Cross-session thread tracking |
|
|
272
|
-
| `plugin/src/index.ts` | ~590 | - |
|
|
280
|
+
| `plugin/src/index.ts` | ~590 | - | `hypercompositor` context engine plugin + window invalidation |
|
|
281
|
+
| `memory-plugin/src/index.ts` | ~290 | - | `hypermem` memory slot plugin (memory_search via hybrid retrieval) |
|
|
273
282
|
|
|
274
283
|
## Test Coverage (105 assertions, 11 suites)
|
|
275
284
|
|
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ High-signal turns are marked as keystones and survive pressure trimming ahead of
|
|
|
140
140
|
|
|
141
141
|
## hyperform
|
|
142
142
|
|
|
143
|
-
Raw model output has two problems. It drifts from your standards (sycophancy, hedging, pagination, formatting) and it drifts from your facts (confabulation, contradiction, stale claims). hyperform handles both.
|
|
143
|
+
Raw model output has two problems. It drifts from your standards (sycophancy, hedging, pagination, formatting) and it drifts from your facts (confabulation, contradiction, stale claims). hyperform handles both: normalization enforces consistency, confabulation resistance checks output against what's actually stored.
|
|
144
144
|
|
|
145
145
|
**Normalization** shapes output to match a profile you define. Three presets ship with hypermem:
|
|
146
146
|
|
|
@@ -175,7 +175,7 @@ tool context, and leave ~30k as allocator reserve. hypermem handles slot competi
|
|
|
175
175
|
automatically -- set contextWindowReserve to your preferred floor and let the compositor fill.
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
**
|
|
178
|
+
**Confabulation resistance** checks output against stored facts before claims are recorded. No LLM call. Pattern matching against the fact corpus, with confidence scoring and contradiction detection. Unsupported claims are flagged, contradictions surface in diagnostics, and a confabulation risk score is attached to the stored episode.
|
|
179
179
|
|
|
180
180
|
---
|
|
181
181
|
|
|
@@ -256,7 +256,16 @@ L1 and L4 structured retrieval are sub-millisecond. Vector embeddings are comput
|
|
|
256
256
|
|
|
257
257
|
## Architecture
|
|
258
258
|
|
|
259
|
-
hypermem plugs into OpenClaw
|
|
259
|
+
hypermem plugs into OpenClaw via two plugins that fill both composition slots:
|
|
260
|
+
|
|
261
|
+
| Plugin | ID | Slot | What it does |
|
|
262
|
+
|---|---|---|---|
|
|
263
|
+
| `@psiclawops/hypercompositor` | `hypercompositor` | `contextEngine` | Owns session lifecycle, ingest, compose, afterTurn indexing, tool compression, hyperform |
|
|
264
|
+
| `@psiclawops/hypermem-memory` | `hypermem` | `memory` | Provides `memory_search` tool backed by hybrid FTS5 + KNN retrieval against library.db |
|
|
265
|
+
|
|
266
|
+
Both load from the same repo and share a single HyperMem core singleton. The context engine plugin (`hypercompositor`) is the heavy one: session warming, compositor, tool gradient, hyperform. The memory plugin (`hypermem`) is a thin wrapper that exposes HyperMem's hybrid retrieval as OpenClaw's standard `MemoryPluginCapability`, so `memory_search` routes through the official memory slot and shows correctly in `openclaw plugins list`.
|
|
267
|
+
|
|
268
|
+
The Plugin column is the npm package name. The ID column is what goes in `plugins.allow` and `plugins.slots.*`. Don't put the package name in a slot config.
|
|
260
269
|
|
|
261
270
|
**L1: SQLite in-memory.** Sub-millisecond hot reads, no network dependency, no daemon, no retry logic. Identity, compressed session history, cached embeddings, topic-scoped session and recall state, and agent registry data. The compositor hits this first on every turn.
|
|
262
271
|
|
|
@@ -276,7 +285,7 @@ FTS5 queries use compound indexes on `agentId + sort key` and prefix optimizatio
|
|
|
276
285
|
|
|
277
286
|
| Collection | What it holds |
|
|
278
287
|
|---|---|
|
|
279
|
-
| Facts |
|
|
288
|
+
| Facts | Claims with confidence scoring, domain, expiry, supersedes chains |
|
|
280
289
|
| Knowledge | Domain/key/value structured data with full-text search |
|
|
281
290
|
| Episodes | Significant events with impact scores and participant tracking |
|
|
282
291
|
| Topics | Cross-session thread tracking and synthesized wiki pages |
|
|
@@ -371,10 +380,12 @@ git clone https://github.com/PsiClawOps/hypermem.git ~/.openclaw/workspace/repo/
|
|
|
371
380
|
cd ~/.openclaw/workspace/repo/hypermem
|
|
372
381
|
npm install && npm run build
|
|
373
382
|
npm --prefix plugin install && npm --prefix plugin run build
|
|
383
|
+
npm --prefix memory-plugin install && npm --prefix memory-plugin run build
|
|
374
384
|
|
|
375
|
-
openclaw config set plugins.slots.contextEngine
|
|
385
|
+
openclaw config set plugins.slots.contextEngine hypercompositor
|
|
376
386
|
openclaw config set plugins.slots.memory hypermem
|
|
377
|
-
openclaw config set plugins.load.paths '["~/.openclaw/workspace/repo/hypermem/plugin"]' --strict-json
|
|
387
|
+
openclaw config set plugins.load.paths '["~/.openclaw/workspace/repo/hypermem/plugin","~/.openclaw/workspace/repo/hypermem/memory-plugin"]' --strict-json
|
|
388
|
+
openclaw config set plugins.allow '["hypercompositor","hypermem"]' --strict-json
|
|
378
389
|
openclaw gateway restart
|
|
379
390
|
```
|
|
380
391
|
|
|
@@ -486,6 +497,19 @@ const spawn = await buildSpawnContext(
|
|
|
486
497
|
|
|
487
498
|
---
|
|
488
499
|
|
|
500
|
+
## CLI
|
|
501
|
+
|
|
502
|
+
`bin/hypermem-status.mjs` provides health checks and metrics from the command line:
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
node bin/hypermem-status.mjs # full dashboard
|
|
506
|
+
node bin/hypermem-status.mjs --agent forge # scoped to one agent
|
|
507
|
+
node bin/hypermem-status.mjs --json # machine-readable output
|
|
508
|
+
node bin/hypermem-status.mjs --health # health checks only (exit 1 on failure)
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
489
513
|
## Data directory
|
|
490
514
|
|
|
491
515
|
```text
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* hypermem status — health check and metrics dashboard CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node bin/hypermem-status.mjs # full dashboard
|
|
7
|
+
* node bin/hypermem-status.mjs --agent forge # scoped to one agent
|
|
8
|
+
* node bin/hypermem-status.mjs --json # machine-readable output
|
|
9
|
+
* node bin/hypermem-status.mjs --health # health checks only (exit 1 on failure)
|
|
10
|
+
*
|
|
11
|
+
* Requires: compiled dist/ (run `npm run build` first)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { resolve, join, dirname } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
|
|
19
|
+
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const root = resolve(__dir, '..');
|
|
21
|
+
|
|
22
|
+
// ── Arg parsing ──────────────────────────────────────────────────
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const flags = {
|
|
25
|
+
json: args.includes('--json'),
|
|
26
|
+
health: args.includes('--health'),
|
|
27
|
+
help: args.includes('--help') || args.includes('-h'),
|
|
28
|
+
agent: null,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const agentIdx = args.indexOf('--agent');
|
|
32
|
+
if (agentIdx !== -1 && args[agentIdx + 1]) {
|
|
33
|
+
flags.agent = args[agentIdx + 1];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (flags.help) {
|
|
37
|
+
console.log(`
|
|
38
|
+
hypermem status — health check and metrics dashboard
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
hypermem-status.mjs [options]
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--agent <id> Scope metrics to a specific agent
|
|
45
|
+
--json Output raw JSON instead of formatted summary
|
|
46
|
+
--health Health checks only (exits 1 if any check fails)
|
|
47
|
+
-h, --help Show this help
|
|
48
|
+
`);
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Resolve data directory ───────────────────────────────────────
|
|
53
|
+
const dataDir = process.env.HYPERMEM_DATA_DIR
|
|
54
|
+
|| join(process.env.HOME || os.homedir(), '.openclaw', 'hypermem');
|
|
55
|
+
|
|
56
|
+
if (!existsSync(dataDir)) {
|
|
57
|
+
console.error(`Error: data directory not found: ${dataDir}`);
|
|
58
|
+
console.error('Is HyperMem installed? Set HYPERMEM_DATA_DIR if using a custom path.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Open DBs ─────────────────────────────────────────────────────
|
|
63
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
64
|
+
|
|
65
|
+
function openDb(filePath, label) {
|
|
66
|
+
if (!existsSync(filePath)) {
|
|
67
|
+
console.error(`Error: ${label} not found: ${filePath}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const db = new DatabaseSync(filePath, { open: true });
|
|
72
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
73
|
+
db.exec('PRAGMA busy_timeout = 3000');
|
|
74
|
+
return db;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(`Error opening ${label}: ${err.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Main DB: pick any agent's messages.db for composition stats, or fall back
|
|
82
|
+
// For fleet-wide, we need at least one agent's message db.
|
|
83
|
+
// The metrics dashboard expects a "main" db — find one.
|
|
84
|
+
let mainDbPath;
|
|
85
|
+
|
|
86
|
+
if (flags.agent) {
|
|
87
|
+
mainDbPath = join(dataDir, 'agents', flags.agent, 'messages.db');
|
|
88
|
+
} else {
|
|
89
|
+
// Find first available agent messages.db
|
|
90
|
+
const agentsDir = join(dataDir, 'agents');
|
|
91
|
+
if (existsSync(agentsDir)) {
|
|
92
|
+
const { readdirSync } = await import('node:fs');
|
|
93
|
+
const agents = readdirSync(agentsDir, { withFileTypes: true })
|
|
94
|
+
.filter(d => d.isDirectory())
|
|
95
|
+
.map(d => d.name);
|
|
96
|
+
for (const a of agents) {
|
|
97
|
+
const candidate = join(agentsDir, a, 'messages.db');
|
|
98
|
+
if (existsSync(candidate)) {
|
|
99
|
+
mainDbPath = candidate;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!mainDbPath || !existsSync(mainDbPath)) {
|
|
107
|
+
console.error('Error: no agent messages.db found. Has HyperMem ingested any sessions?');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const libraryDbPath = join(dataDir, 'library.db');
|
|
112
|
+
|
|
113
|
+
const mainDb = openDb(mainDbPath, 'messages.db');
|
|
114
|
+
const libraryDb = openDb(libraryDbPath, 'library.db');
|
|
115
|
+
|
|
116
|
+
// ── Import metrics functions ─────────────────────────────────────
|
|
117
|
+
const distPath = join(root, 'dist', 'metrics-dashboard.js');
|
|
118
|
+
if (!existsSync(distPath)) {
|
|
119
|
+
console.error('Error: dist/metrics-dashboard.js not found. Run `npm run build` first.');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const { collectMetrics, formatMetricsSummary } = await import(distPath);
|
|
124
|
+
|
|
125
|
+
// ── Collect and output ───────────────────────────────────────────
|
|
126
|
+
const opts = {};
|
|
127
|
+
if (flags.agent) {
|
|
128
|
+
opts.agentIds = [flags.agent];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const metrics = await collectMetrics(mainDb, libraryDb, opts);
|
|
133
|
+
|
|
134
|
+
if (flags.health) {
|
|
135
|
+
// Health-only mode: check and exit
|
|
136
|
+
const h = metrics.health;
|
|
137
|
+
const ok = h.mainDbOk && h.libraryDbOk && (h.cacheOk === null || h.cacheOk);
|
|
138
|
+
|
|
139
|
+
if (flags.json) {
|
|
140
|
+
console.log(JSON.stringify(h, null, 2));
|
|
141
|
+
} else {
|
|
142
|
+
console.log(`hypermem ${h.packageVersion} health check`);
|
|
143
|
+
console.log(` main db: ${h.mainDbOk ? '✅' : '❌'}${h.mainSchemaVersion !== null ? ` (schema v${h.mainSchemaVersion})` : ''}`);
|
|
144
|
+
console.log(` library db: ${h.libraryDbOk ? '✅' : '❌'}${h.librarySchemaVersion !== null ? ` (schema v${h.librarySchemaVersion})` : ''}`);
|
|
145
|
+
if (h.cacheOk !== null) {
|
|
146
|
+
console.log(` cache: ${h.cacheOk ? '✅' : '❌'}`);
|
|
147
|
+
}
|
|
148
|
+
console.log(` status: ${ok ? '✅ healthy' : '❌ degraded'}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
process.exit(ok ? 0 : 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (flags.json) {
|
|
155
|
+
console.log(JSON.stringify(metrics, null, 2));
|
|
156
|
+
} else {
|
|
157
|
+
console.log(formatMetricsSummary(metrics));
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.error(`Error collecting metrics: ${err.message}`);
|
|
161
|
+
if (err.stack) console.error(err.stack);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
} finally {
|
|
164
|
+
try { mainDb.close(); } catch {}
|
|
165
|
+
try { libraryDb.close(); } catch {}
|
|
166
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Background Indexer
|
|
3
|
+
*
|
|
4
|
+
* Processes message history to extract structured knowledge:
|
|
5
|
+
* - Facts: atomic pieces of learned information
|
|
6
|
+
* - Episodes: significant events worth remembering
|
|
7
|
+
* - Topics: conversation threads and their lifecycle
|
|
8
|
+
* - Knowledge: durable structured entries (domain + key)
|
|
9
|
+
*
|
|
10
|
+
* Runs as a periodic background task, processing unindexed messages
|
|
11
|
+
* in batches. Each batch is scored, classified, and stored in L4 (library.db).
|
|
12
|
+
*
|
|
13
|
+
* Design principles:
|
|
14
|
+
* - No LLM dependency: extraction uses pattern matching + heuristics
|
|
15
|
+
* - Idempotent: tracks watermarks per agent to avoid reprocessing
|
|
16
|
+
* - Bounded: processes N messages per tick to avoid blocking
|
|
17
|
+
* - Observable: logs extraction stats for monitoring
|
|
18
|
+
*/
|
|
19
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
20
|
+
import type { IndexerConfig, SessionCursor } from './types.js';
|
|
21
|
+
import { type DreamerConfig } from './dreaming-promoter.js';
|
|
22
|
+
import type { VectorStore } from './vector-store.js';
|
|
23
|
+
export interface IndexerStats {
|
|
24
|
+
agentId: string;
|
|
25
|
+
messagesProcessed: number;
|
|
26
|
+
factsExtracted: number;
|
|
27
|
+
episodesRecorded: number;
|
|
28
|
+
topicsUpdated: number;
|
|
29
|
+
knowledgeUpserted: number;
|
|
30
|
+
/** Number of superseded fact vectors tombstoned from the vector index this tick. */
|
|
31
|
+
tombstoned: number;
|
|
32
|
+
elapsedMs: number;
|
|
33
|
+
/** Number of messages that were post-cursor (unseen by model, high-signal priority). */
|
|
34
|
+
postCursorMessages: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Optional callback to fetch the session cursor for an agent+session.
|
|
38
|
+
* When provided, the indexer uses the cursor to prioritize unseen messages.
|
|
39
|
+
* The cursor boundary separates "model has seen this" from "new since last compose".
|
|
40
|
+
*/
|
|
41
|
+
export type CursorFetcher = (agentId: string, sessionKey: string) => Promise<SessionCursor | null>;
|
|
42
|
+
export interface WatermarkState {
|
|
43
|
+
agentId: string;
|
|
44
|
+
lastMessageId: number;
|
|
45
|
+
lastRunAt: string;
|
|
46
|
+
}
|
|
47
|
+
export declare class BackgroundIndexer {
|
|
48
|
+
private getMessageDb?;
|
|
49
|
+
private getLibraryDb?;
|
|
50
|
+
private listAgents?;
|
|
51
|
+
private getCursor?;
|
|
52
|
+
private readonly config;
|
|
53
|
+
private readonly dreamerConfig;
|
|
54
|
+
private intervalHandle;
|
|
55
|
+
private running;
|
|
56
|
+
private vectorStore;
|
|
57
|
+
private synthesizer;
|
|
58
|
+
private tickCount;
|
|
59
|
+
constructor(config?: Partial<IndexerConfig>, getMessageDb?: ((agentId: string) => DatabaseSync) | undefined, getLibraryDb?: (() => DatabaseSync) | undefined, listAgents?: (() => string[]) | undefined, getCursor?: CursorFetcher | undefined, dreamerConfig?: Partial<DreamerConfig>);
|
|
60
|
+
/**
|
|
61
|
+
* Set the vector store for embedding new facts/episodes at index time.
|
|
62
|
+
* Optional — if not set, indexer runs without embedding (FTS5-only mode).
|
|
63
|
+
*/
|
|
64
|
+
setVectorStore(vs: VectorStore): void;
|
|
65
|
+
/**
|
|
66
|
+
* Start periodic indexing.
|
|
67
|
+
*/
|
|
68
|
+
start(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Stop periodic indexing.
|
|
71
|
+
*/
|
|
72
|
+
stop(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Run one indexing pass across all agents.
|
|
75
|
+
*/
|
|
76
|
+
tick(): Promise<IndexerStats[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Process a single agent's unindexed messages.
|
|
79
|
+
*
|
|
80
|
+
* When a cursor fetcher is available, messages are split into two tiers:
|
|
81
|
+
* - Post-cursor (id > cursor.lastSentId): "unseen" by the model, high-signal priority
|
|
82
|
+
* - Pre-cursor (id <= cursor.lastSentId): already in the model's context window, lower priority
|
|
83
|
+
* Post-cursor messages are processed first. This ensures the indexer prioritizes
|
|
84
|
+
* content the model hasn't seen yet — decisions, incidents, and discoveries that
|
|
85
|
+
* happened between context windows.
|
|
86
|
+
*/
|
|
87
|
+
private processAgent;
|
|
88
|
+
/**
|
|
89
|
+
* Fetch unindexed messages for an agent.
|
|
90
|
+
*/
|
|
91
|
+
private getUnindexedMessages;
|
|
92
|
+
/**
|
|
93
|
+
* Get the session key for a conversation ID.
|
|
94
|
+
*/
|
|
95
|
+
private getSessionKeyForMessage;
|
|
96
|
+
/**
|
|
97
|
+
* Get the indexing watermark for an agent.
|
|
98
|
+
*/
|
|
99
|
+
private getWatermark;
|
|
100
|
+
/**
|
|
101
|
+
* Set the indexing watermark for an agent.
|
|
102
|
+
*/
|
|
103
|
+
private setWatermark;
|
|
104
|
+
/**
|
|
105
|
+
* Apply time-based decay to facts.
|
|
106
|
+
* Increases decay_score for older facts, making them less relevant.
|
|
107
|
+
*/
|
|
108
|
+
private applyDecay;
|
|
109
|
+
/**
|
|
110
|
+
* Parse a duration string like "24h", "7d" into seconds.
|
|
111
|
+
*/
|
|
112
|
+
private parseDuration;
|
|
113
|
+
/**
|
|
114
|
+
* One-time backfill: embed episodes with sig>=0.5 that were missed by the
|
|
115
|
+
* old >=0.7 vectorization threshold.
|
|
116
|
+
*
|
|
117
|
+
* Gated by a system_state flag 'indexer:episode_backfill_v1' so it runs
|
|
118
|
+
* exactly once even across gateway restarts. Safe to re-run manually
|
|
119
|
+
* (delete the flag row first) if re-backfill is ever needed.
|
|
120
|
+
*/
|
|
121
|
+
backfillEpisodeVectors(): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Get current watermarks for all agents.
|
|
124
|
+
*/
|
|
125
|
+
getWatermarks(libraryDb: DatabaseSync): WatermarkState[];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Create and start a background indexer connected to hypermem databases.
|
|
129
|
+
* Used by the hook or a standalone daemon.
|
|
130
|
+
*/
|
|
131
|
+
export declare function createIndexer(getMessageDb: (agentId: string) => DatabaseSync, getLibraryDb: () => DatabaseSync, listAgents: () => string[], config?: Partial<IndexerConfig>, getCursor?: CursorFetcher, vectorStore?: VectorStore, dreamerConfig?: Partial<DreamerConfig>): BackgroundIndexer;
|
|
132
|
+
//# sourceMappingURL=background-indexer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"background-indexer.d.ts","sourceRoot":"","sources":["../src/background-indexer.ts"],"names":[],"mappings":"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"}
|