@psiclawops/hypermem 0.8.1 → 0.8.3

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/README.md CHANGED
@@ -8,10 +8,20 @@
8
8
 
9
9
  hypermem is a SQLite-backed runtime context engine for OpenClaw agents.
10
10
 
11
+ **Quick install** (interactive, detects hardware, writes config):
12
+
13
+ ```bash
14
+ npm install @psiclawops/hypermem && npx hypermem-install
15
+ ```
16
+
17
+ Or via the shell installer:
18
+
11
19
  ```bash
12
20
  curl -fsSL https://raw.githubusercontent.com/PsiClawOps/hypermem/main/install.sh | bash
13
21
  ```
14
22
 
23
+ Or install manually via `npm install @psiclawops/hypermem` — see [Installation](#installation) for plugin wiring, embedding setup, and step-by-step paths.
24
+
15
25
 
16
26
  ---
17
27
 
@@ -376,7 +386,7 @@ Slot-level budget allocation is shown in the [hypercompositor diagram](#what-the
376
386
 
377
387
  ## Requirements
378
388
 
379
- **Current release: hypermem 0.8.1.** Changelog: [CHANGELOG.md](./CHANGELOG.md)
389
+ **Current release: hypermem 0.8.2.** Changelog: [CHANGELOG.md](./CHANGELOG.md)
380
390
 
381
391
  | Requirement | Version | Notes |
382
392
  |---|---|---|
@@ -389,7 +399,7 @@ SQLite is a library, not a service. All four layers run in-process with no exter
389
399
  **Runtime version constants** (importable from the package):
390
400
  ```typescript
391
401
  import {
392
- ENGINE_VERSION, // '0.8.1'
402
+ ENGINE_VERSION, // '0.8.2'
393
403
  MIN_NODE_VERSION, // '22.0.0'
394
404
  SQLITE_VEC_VERSION, // '0.1.9'
395
405
  MAIN_SCHEMA_VERSION, // 10 (messages.db)
@@ -405,7 +415,39 @@ Schema versions are stamped into each database on startup and checked on open. A
405
415
 
406
416
  **Requirements:** Node.js 22+, OpenClaw with context engine plugin support. No standalone SQLite install needed (uses Node 22 built-in `node:sqlite`). Embedding provider is optional for first install.
407
417
 
408
- ### From source
418
+ hypermem works two ways:
419
+ - **As a library** — import directly into your own Node.js code. No OpenClaw required.
420
+ - **As an OpenClaw plugin** — replaces the default context engine. Requires a running OpenClaw gateway.
421
+
422
+ ### Library usage (no OpenClaw required)
423
+
424
+ ```bash
425
+ npm install @psiclawops/hypermem
426
+ ```
427
+
428
+ ```typescript
429
+ import { HyperMem } from '@psiclawops/hypermem';
430
+ import { join } from 'node:path';
431
+ import { homedir } from 'node:os';
432
+
433
+ const hm = await HyperMem.create({
434
+ dataDir: join(homedir(), '.openclaw', 'hypermem'),
435
+ embedding: { provider: 'none' },
436
+ });
437
+
438
+ await hm.recordUserMessage('my-agent', 'session-1', 'Hello');
439
+ const composed = await hm.compose({
440
+ agentId: 'my-agent',
441
+ sessionKey: 'session-1',
442
+ prompt: 'Hello',
443
+ tokenBudget: 4000,
444
+ provider: 'anthropic',
445
+ });
446
+ ```
447
+
448
+ That's it. No gateway, no plugins, no config files. See [API](#api) for the full interface.
449
+
450
+ ### OpenClaw plugin install (from source)
409
451
 
410
452
  ```bash
411
453
  git clone https://github.com/PsiClawOps/hypermem.git
@@ -433,15 +475,30 @@ This sets lightweight mode (FTS5 keyword search, no embedding provider needed).
433
475
 
434
476
  Wire the plugins into OpenClaw:
435
477
 
478
+ > **⚠️ Merge, don't overwrite.** If you already have values in `plugins.load.paths` or `plugins.allow`, check them first and include your existing entries alongside the new ones. Replacing the list drops whatever was there before.
479
+ >
480
+ > ```bash
481
+ > openclaw config get plugins.allow
482
+ > openclaw config get plugins.load.paths
483
+ > ```
484
+
436
485
  ```bash
437
- openclaw config set plugins.load.paths "[\"$HOME/.openclaw/plugins/hypermem/plugin\",\"$HOME/.openclaw/plugins/hypermem/memory-plugin\"]" --strict-json
486
+ # Use a variable to avoid shell quote-escaping issues with $HOME:
487
+ HYPERMEM_PATHS="[\"${HOME}/.openclaw/plugins/hypermem/plugin\",\"${HOME}/.openclaw/plugins/hypermem/memory-plugin\"]"
488
+ openclaw config set plugins.load.paths "$HYPERMEM_PATHS" --strict-json
489
+ # If you have existing load paths, merge them into the array in HYPERMEM_PATHS.
490
+
438
491
  openclaw config set plugins.slots.contextEngine hypercompositor
439
492
  openclaw config set plugins.slots.memory hypermem
493
+
494
+ # ⚠️ Add to your existing plugins.allow — do not replace your current list.
495
+ # Edit the array below to include any plugins you already have allowed:
440
496
  openclaw config set plugins.allow '["hypercompositor","hypermem"]' --strict-json
497
+
441
498
  openclaw gateway restart
442
499
  ```
443
500
 
444
- Verify (run from the repo clone directory):
501
+ Verify (run these commands from the repo clone directory — `bin/` is a relative path):
445
502
 
446
503
  ```bash
447
504
  openclaw plugins list # hypercompositor and hypermem should show as loaded
@@ -530,9 +587,11 @@ Full reference: **[docs/TUNING.md](./docs/TUNING.md)**
530
587
 
531
588
  ```typescript
532
589
  import { HyperMem } from '@psiclawops/hypermem';
590
+ import { join } from 'node:path';
591
+ import { homedir } from 'node:os';
533
592
 
534
593
  const hm = await HyperMem.create({
535
- dataDir: '~/.openclaw/hypermem',
594
+ dataDir: join(homedir(), '.openclaw', 'hypermem'),
536
595
  cache: { maxEntries: 10000 },
537
596
  // Local (Ollama):
538
597
  embedding: { ollamaUrl: 'http://localhost:11434', model: 'nomic-embed-text' },
@@ -581,6 +640,14 @@ node bin/hypermem-status.mjs --json # machine-readable output
581
640
  node bin/hypermem-status.mjs --health # health checks only (exit 1 on failure)
582
641
  ```
583
642
 
643
+ By default, `hypermem-status` looks for data in `~/.openclaw/hypermem`. If your data directory is elsewhere (e.g. testing in an isolated environment), set:
644
+
645
+ ```bash
646
+ HYPERMEM_DATA_DIR=/path/to/data node bin/hypermem-status.mjs --health
647
+ ```
648
+
649
+ > **Fresh install note:** If no agent has run a session yet, `--health` will report "no sessions ingested" rather than a database error. This is expected. Send a test message to any agent, then re-run the health check.
650
+
584
651
  ---
585
652
 
586
653
  ## Pressure management
@@ -612,6 +679,22 @@ hypermem composes context fresh on every turn, but a long-running session still
612
679
 
613
680
  ---
614
681
 
682
+ ## Common issues
683
+
684
+ | Symptom | Cause | Fix |
685
+ |---|---|---|
686
+ | `falling back to default engine "legacy"` in logs | Plugin not loaded or slot misconfigured | Check `openclaw config get plugins.slots.contextEngine` is `hypercompositor`, paths are correct, and both plugins are in `plugins.allow` |
687
+ | `openclaw gateway restart` says disabled/not configured | OpenClaw not fully onboarded | Complete OpenClaw setup first. `gateway restart` requires a running gateway. |
688
+ | `openclaw logs` fails with auth/token error | Gateway auth not set up for CLI | Run `openclaw gateway status` to confirm the gateway is accessible |
689
+ | `facts=0 semantic=0` every turn | Fresh install, no data yet | Expected. Facts accumulate over real conversations. |
690
+ | Health check says "no sessions ingested" | No agent has run a session yet | Send a test message, then re-run |
691
+ | JS code creates `./~/.openclaw/` directory | Used literal `~` in JS instead of `homedir()` | Use `join(homedir(), '.openclaw', 'hypermem')` from `node:path` and `node:os` |
692
+ | `INSTALL.md` not found in npm package | Older published version | Update to latest or read INSTALL.md on [GitHub](https://github.com/PsiClawOps/hypermem/blob/main/INSTALL.md) |
693
+
694
+ Full troubleshooting: **[INSTALL.md § Troubleshooting](./INSTALL.md#troubleshooting)**
695
+
696
+ ---
697
+
615
698
  ## Migration
616
699
 
617
700
  hypermem doesn't touch your existing memory data. Install it, switch the context engine, and migrate historical data on your own timeline.
@@ -104,6 +104,16 @@ if (flags.agent) {
104
104
  }
105
105
 
106
106
  if (!mainDbPath || !existsSync(mainDbPath)) {
107
+ if (args.includes('--health') || args.includes('--json')) {
108
+ const result = { status: 'no_sessions', message: 'Installed but no agent sessions ingested yet. Send a message to any agent, then re-run.' };
109
+ if (args.includes('--json')) {
110
+ console.log(JSON.stringify(result, null, 2));
111
+ } else {
112
+ console.log('Status: installed, no sessions ingested yet.');
113
+ console.log('Send a message to any agent, then re-run this health check.');
114
+ }
115
+ process.exit(0);
116
+ }
107
117
  console.error('Error: no agent messages.db found. Has HyperMem ingested any sessions?');
108
118
  process.exit(1);
109
119
  }
package/dist/version.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Release version — matches package.json and is stamped into library.db on every startup. */
2
- export declare const ENGINE_VERSION = "0.8.1";
2
+ export declare const ENGINE_VERSION = "0.8.2";
3
3
  /** Minimum Node.js version required — matches package.json engines field. */
4
4
  export declare const MIN_NODE_VERSION = "22.0.0";
5
5
  /** @deprecated No longer used — Redis was replaced with SQLite :memory: CacheLayer. */
@@ -19,15 +19,15 @@ export declare const LIBRARY_SCHEMA_VERSION_EXPORT = 19;
19
19
  /**
20
20
  * Compatibility version — the single number operators and consumers check.
21
21
  * Maps to: messages.db schema v10, library schema v19.
22
- * Matches ENGINE_VERSION for the 0.8.1 release.
22
+ * Matches ENGINE_VERSION for the 0.8.2 release.
23
23
  */
24
- export declare const HYPERMEM_COMPAT_VERSION = "0.8.1";
24
+ export declare const HYPERMEM_COMPAT_VERSION = "0.8.2";
25
25
  /**
26
26
  * Schema compatibility map — machine-readable version requirements.
27
27
  * Use this to verify DB schemas match the running engine.
28
28
  */
29
29
  export declare const SCHEMA_COMPAT: {
30
- readonly compatVersion: "0.8.1";
30
+ readonly compatVersion: "0.8.2";
31
31
  readonly mainSchema: 10;
32
32
  readonly librarySchema: 19;
33
33
  };
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Release version — matches package.json and is stamped into library.db on every startup. */
2
- export const ENGINE_VERSION = '0.8.1';
2
+ export const ENGINE_VERSION = '0.8.2';
3
3
  /** Minimum Node.js version required — matches package.json engines field. */
4
4
  export const MIN_NODE_VERSION = '22.0.0';
5
5
  /** @deprecated No longer used — Redis was replaced with SQLite :memory: CacheLayer. */
@@ -19,15 +19,15 @@ export const LIBRARY_SCHEMA_VERSION_EXPORT = 19;
19
19
  /**
20
20
  * Compatibility version — the single number operators and consumers check.
21
21
  * Maps to: messages.db schema v10, library schema v19.
22
- * Matches ENGINE_VERSION for the 0.8.1 release.
22
+ * Matches ENGINE_VERSION for the 0.8.2 release.
23
23
  */
24
- export const HYPERMEM_COMPAT_VERSION = '0.8.1';
24
+ export const HYPERMEM_COMPAT_VERSION = '0.8.2';
25
25
  /**
26
26
  * Schema compatibility map — machine-readable version requirements.
27
27
  * Use this to verify DB schemas match the running engine.
28
28
  */
29
29
  export const SCHEMA_COMPAT = {
30
- compatVersion: '0.8.1',
30
+ compatVersion: '0.8.2',
31
31
  mainSchema: 10,
32
32
  librarySchema: 19,
33
33
  };
@@ -0,0 +1,33 @@
1
+ # API Stability — hypermem
2
+
3
+ ## Public API (frozen at 0.5.0)
4
+
5
+ The following four methods are the stable public surface of hypermem. They are
6
+ frozen as of 0.5.0. Breaking changes require a 1.0.0 semver bump — no exceptions.
7
+
8
+ | Method | Signature | Description |
9
+ |---|---|---|
10
+ | `create` | `create(config: HypermemConfig): HypermemInstance` | Initialize a hypermem instance |
11
+ | `record` | `record(agentId, sessionKey, message): Promise<void>` | Store a message in the session store |
12
+ | `compose` | `compose(agentId, sessionKey, opts?): Promise<ComposedContext>` | Assemble context for a prompt |
13
+ | `close` | `close(): Promise<void>` | Gracefully shut down, flush, close DB connections |
14
+
15
+ ## What "frozen" means
16
+
17
+ - No argument removal or rename
18
+ - No return type narrowing that breaks existing consumers
19
+ - No behavior change that breaks existing consumers without a deprecation cycle
20
+ - Additive changes (new optional args, new fields in return type) are allowed
21
+
22
+ ## Internal APIs
23
+
24
+ Everything else — `FactStore`, `CacheLayer`, `Compositor`, `HybridRetrieval`,
25
+ `DreamingPromoter`, etc. — is internal and may change between minor versions.
26
+ These are exported for advanced use but carry no stability guarantee until 1.0.
27
+
28
+ ## Versioning policy
29
+
30
+ | Version range | Policy |
31
+ |---|---|
32
+ | `0.x.y` | Public API frozen, internal APIs may change on minor bumps |
33
+ | `1.0.0+` | Full semver — breaking changes require major bump |
@@ -0,0 +1,35 @@
1
+ # Known Limitations — HyperMem 0.8.0
2
+
3
+ ## Global-scope fact write authorization
4
+
5
+ Facts written with `scope='global'` are readable fleet-wide at L4 priority. The write path has no authorization gate — any agent with access to the HyperMem API can write a global-scope fact.
6
+
7
+ **Impact:** In a trusted single-operator deployment (the intended target), this is acceptable. All agents are operator-controlled and share the same trust boundary.
8
+
9
+ **Planned fix:** Introduce a write-authority model that gates global-scope writes to designated agents (council seats or explicitly allowlisted agent IDs). See [docs/ROADMAP.md](ROADMAP.md).
10
+
11
+ **Workaround:** Restrict HyperMem API access to trusted agents only. Do not expose the API to untrusted external agents.
12
+
13
+ ## Cross-agent org registry is hardcoded
14
+
15
+ `visibilityFilter()` in `cross-agent.ts` resolves agent tiers and visibility from a hardcoded `defaultOrgRegistry()`. This duplicates fleet topology that lives authoritatively in `fleet_agents` + `fleet_orgs` in library.db.
16
+
17
+ **Impact:** New agents added to library.db but not the hardcoded registry get fleet-only visibility until the registry is updated in code.
18
+
19
+ **Planned fix:** Live-load registry from library.db on startup, hardcoded as cold-start fallback only. See [docs/ROADMAP.md](ROADMAP.md).
20
+
21
+ ## Cursor durability across restarts
22
+
23
+ The compositor writes a session cursor to hot cache (SQLite `:memory:`) with a 24h TTL. On gateway restart, the cursor is lost until the next `compose()` call repopulates it.
24
+
25
+ **Impact:** Background indexer may miss the cursor on the first turn after a restart, falling back to full-history scan.
26
+
27
+ **Planned fix:** Dual-write cursor to messages.db so it survives restarts. See [docs/ROADMAP.md](ROADMAP.md).
28
+
29
+ ## Cross-session context has no boundary markers
30
+
31
+ `buildCrossSessionContext()` renders flat previews with no per-message boundaries or sender identity labels.
32
+
33
+ **Impact:** Context from different sessions blends together, making attribution ambiguous in multi-agent scenarios.
34
+
35
+ **Planned fix:** WQ-20260402-001. See [docs/ROADMAP.md](ROADMAP.md).
@@ -0,0 +1,243 @@
1
+ # MEMORY.md authoring
2
+
3
+ `MEMORY.md` is an index, not a dump.
4
+
5
+ Use it to preserve durable signal the agent should keep noticing across sessions: decisions, operating rules, important architecture facts, stable paths, active plans, and search pointers into deeper history.
6
+
7
+ Do not use it as a transcript, changelog, or a second database. hypermem already stores the raw history. `MEMORY.md` should make the right things easy to find again.
8
+
9
+ ## What belongs in MEMORY.md
10
+
11
+ Keep items that are still likely to matter after the current session ends:
12
+
13
+ - stable architectural facts
14
+ - standing rules and constraints
15
+ - current project plans worth reloading next session
16
+ - important decisions with enough context to recover why they happened
17
+ - durable paths, repo locations, and operational references
18
+ - search pointers for deeper recall
19
+
20
+ Good rule: if losing the item would make the next session slower, riskier, or more error-prone, it probably belongs.
21
+
22
+ ## What does not belong
23
+
24
+ Do not put these in `MEMORY.md`:
25
+
26
+ - full conversation summaries
27
+ - test logs and build output
28
+ - one-off debugging notes with no ongoing value
29
+ - temporary status that belongs in a daily checkpoint
30
+ - long implementation detail blocks copied from code or specs
31
+ - duplicate facts already obvious from repo structure
32
+
33
+ If the value is only "this happened today," it usually belongs in the daily file, not the index.
34
+
35
+ ## Authoring style
36
+
37
+ Write pointer-first entries. One line should stand on its own, and one search pointer should reopen the deeper context.
38
+
39
+ Preferred format:
40
+
41
+ ```md
42
+ - TUNE-011: indexer quality gate, 33% fact noise removed (2527→1707 facts)
43
+ → memory_search("TUNE-011 indexer quality")
44
+ ```
45
+
46
+ Avoid this:
47
+
48
+ ```md
49
+ - TUNE-011
50
+ - changed isQualityFact()
51
+ - added guards for code blocks
52
+ - adjusted token thresholds
53
+ - updated tests
54
+ - shipped at 11:42 PM
55
+ ```
56
+
57
+ The first format reloads context fast. The second turns `MEMORY.md` into a bad replica of history.
58
+
59
+ ## Recommended sections
60
+
61
+ Most agents do well with some version of these:
62
+
63
+ - Identity
64
+ - Domain priming
65
+ - Key specs and plans
66
+ - Standing rules
67
+ - Key decisions
68
+ - Recent actions or active facts, if they are still durable enough to matter
69
+
70
+ Use only the sections that actually carry signal.
71
+
72
+ ## Recency checking
73
+
74
+ Some facts are true now but should not be treated like permanent law.
75
+
76
+ Examples:
77
+
78
+ - current default model provider
79
+ - active fallback model
80
+ - temporary deployment workarounds
81
+ - known incidents, freezes, or suspensions
82
+ - "current release" statements
83
+ - performance numbers tied to a recent benchmark
84
+
85
+ For these, add recency cues and review them on a cadence.
86
+
87
+ ### Mark temporal facts clearly
88
+
89
+ When a fact is time-bound, include one or more of:
90
+
91
+ - an explicit date, such as `as of 2026-04-18`
92
+ - a condition, such as `until Copilot is restored`
93
+ - a review trigger, such as `recheck after next deploy`
94
+ - a search pointer to the deeper event history
95
+
96
+ Example:
97
+
98
+ ```md
99
+ - Fleet model drift checks suspended as of 2026-04-14, until Copilot is restored
100
+ → memory_search("model drift suspension Copilot restored")
101
+ ```
102
+
103
+ ### Use a stale-question test
104
+
105
+ Before adding an item, ask:
106
+
107
+ 1. Will this still be true in 30 days?
108
+ 2. If it changed silently, would stale memory cause a bad decision?
109
+ 3. Does the entry tell the next session how to verify freshness?
110
+
111
+ If the answer pattern is `no / yes / no`, the item needs recency metadata or it belongs somewhere else.
112
+
113
+ ### Review cadence
114
+
115
+ A simple operating rule works well:
116
+
117
+ - review "current state" lines every 7 to 14 days
118
+ - review release, benchmark, and provider facts after each upgrade or deployment change
119
+ - remove or rewrite any entry whose time condition no longer holds
120
+
121
+ ### Prefer ranges over false permanence
122
+
123
+ Bad:
124
+
125
+ ```md
126
+ - Forge runs copilot-local/claude-sonnet-4.6
127
+ ```
128
+
129
+ Better:
130
+
131
+ ```md
132
+ - Forge standard model was copilot-local/claude-sonnet-4.6 as of 2026-04-11; recheck after provider routing changes
133
+ → memory_search("Forge standard model 2026-04-11 provider routing")
134
+ ```
135
+
136
+ ## Relationship to daily memory files
137
+
138
+ Use daily files for checkpoint logging: what changed, what blocked, what to resume.
139
+
140
+ Use `MEMORY.md` for the compact map of durable context.
141
+
142
+ A good daily file helps you resume tomorrow. A good `MEMORY.md` helps you resume next month.
143
+
144
+ ## Maintenance rule
145
+
146
+ If an entry no longer earns its place, delete it.
147
+
148
+ A shorter `MEMORY.md` with current signal beats a larger one full of expired truth.
149
+
150
+ ## Static index vs runtime tail (compositor contract)
151
+
152
+ `MEMORY.md` has two regions. The contract between them is enforced by the
153
+ compositor and must be preserved by anyone editing the file.
154
+
155
+ ```
156
+ ┌─────────────────────────────────────────────┐
157
+ │ Static curated index (human-authored) │
158
+ │ • identity, pointers, durable facts │
159
+ │ • edit this region by hand │
160
+ ├─────────────────────────────────────────────┤
161
+ │ <!-- OPENCLAW_CACHE_BOUNDARY --> │
162
+ ├─────────────────────────────────────────────┤
163
+ │ Runtime tail (compositor-generated) │
164
+ │ • Active Facts │
165
+ │ • Temporal Context │
166
+ │ • Other Active Sessions │
167
+ │ • Recent Actions │
168
+ │ • Dynamic Project Context │
169
+ └─────────────────────────────────────────────┘
170
+ ```
171
+
172
+ ### Rules
173
+
174
+ 1. **On-disk `MEMORY.md` is the curated static index.** It holds identity
175
+ anchors, pointer entries with `memory_search()` hints, and durable facts
176
+ that earn their place long-term.
177
+
178
+ 2. **Everything below `<!-- OPENCLAW_CACHE_BOUNDARY -->` is the compositor's
179
+ runtime tail.** It is regenerated on session warm from live stores: the
180
+ fact table, contradiction audits, topic activity, recent tool actions,
181
+ and dynamic project context. Treat the text below the boundary as a
182
+ snapshot, not a source of truth.
183
+
184
+ 3. **Do not hand-edit the runtime tail.** Anything you write there will be
185
+ overwritten on the next compositor pass.
186
+
187
+ 4. **Do not copy runtime tail content back up into the static index.**
188
+ Facts in the runtime tail originate from the fact store. Copying them
189
+ into the static region:
190
+ - duplicates the content between two layers,
191
+ - hardens a temporary state into a durable record,
192
+ - and bypasses the promoter's quality filters and temporal-marker screen.
193
+
194
+ If a runtime fact genuinely belongs in the durable index, let the
195
+ dreaming promoter handle it, or re-author the claim manually with the
196
+ durability rules above.
197
+
198
+ 5. **The compositor owns the boundary marker.** Do not delete, move, or
199
+ relabel `<!-- OPENCLAW_CACHE_BOUNDARY -->`. Deleting it makes the entire
200
+ file look static to the compositor and breaks the runtime tail rebuild.
201
+
202
+ ### Why this matters
203
+
204
+ The runtime tail exists to surface what is live right now: active facts,
205
+ recent actions, other sessions, current project context. The static index
206
+ exists to hold what will still be true next month. Conflating the two
207
+ produces the exact failure mode the temporal-marker screen guards against:
208
+ temporary state hardens into durable memory, and the agent loses the
209
+ ability to tell "fresh right now" from "true forever."
210
+
211
+ ## Promoter temporal-marker screen
212
+
213
+ The dreaming promoter (`src/dreaming-promoter.ts`) uses a second line of
214
+ defense to keep time-bound facts out of durable memory: the
215
+ temporal-marker screen.
216
+
217
+ **How it works.** When `isPromotable()` evaluates a candidate fact, it
218
+ checks the content against a centralized list of temporal markers:
219
+
220
+ - explicit time bounds: `as of`, `until`, `currently`, `for now`
221
+ - transitional states: `suspended`, `pending`, `paused`, `blocked`, `frozen`
222
+ - rollout language: `rollout`, `phase`, `migration ongoing`, `pre-release`
223
+ - experimental language: `temporary`, `trial`, `experiment(al)`, `exploratory period`, `override`, `hotfix`, `workaround`, `recheck`
224
+ - conditional scope: `in effect during`, `active while … continues/ongoing/rolling out`
225
+
226
+ If any marker matches, the fact must carry structured recency metadata
227
+ (`validFrom` or `invalidAt` on the facts row) to be eligible for durable
228
+ promotion. Plain ISO dates in the content text are **not** a bypass: a
229
+ sentence like `suspended pending X as of 2026-04-18` still contains the
230
+ temporal markers `suspended` and `pending`, and the date only confirms
231
+ that the claim is time-bound.
232
+
233
+ **Extending the marker list.** The list is exported from
234
+ `src/dreaming-promoter.ts` as `TEMPORAL_MARKERS`. Keep it centralized.
235
+ Tests in `test/dreaming-promoter-temporal.mjs` exercise coverage on both
236
+ direct and disguised phrasing; add new fixtures there when adding markers.
237
+
238
+ **Implications for hand-written `MEMORY.md` entries.** The same
239
+ discipline applies at the authoring layer. If you find yourself writing
240
+ "currently," "for now," "pending X," or "in effect during Y" in the
241
+ static index, add an explicit date-stamped condition or move the note to
242
+ a daily file. The promoter's screen is a safety net, not a substitute
243
+ for good authoring.
@@ -0,0 +1,56 @@
1
+ # HyperMem Migration
2
+
3
+ Start here.
4
+
5
+ - **Operator guide:** [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md)
6
+ - **Current repo state:** source-specific migration examples live in `MIGRATION_GUIDE.md`. There is no bundled unified migration dispatcher in this repo yet.
7
+
8
+ All migration examples default to **dry-run** where shown. Add `--apply` only when you are ready to write data.
9
+
10
+ ---
11
+
12
+ ## Multi-operator deployments (breaking change warning)
13
+
14
+ > ⚠️ **KL-01: No global-scope write gate (still open as of 0.8.0)**
15
+ >
16
+ > hypermem 0.5.0 ships without a write gate for `scope='global'` facts. In a
17
+ > **single-operator deployment** (one user, one fleet sharing `library.db`), this
18
+ > is acceptable — agents share context intentionally.
19
+ >
20
+ > In a **multi-operator deployment** (multiple users or tenants sharing one
21
+ > `library.db`), this is a **breaking isolation risk**: a global-scope write from
22
+ > one operator's agent propagates to all other operators' agents.
23
+ >
24
+ > **Do not run hypermem 0.5.0 in a multi-operator deployment without an external
25
+ > write gate at the application layer.** The write gate is deferred to 1.0.0
26
+ > where it will be enforced at the library DB level.
27
+ >
28
+ > A runtime warning is logged whenever `scope='global'` is written — monitor
29
+ > for this in production: `[hypermem] WARNING: ... scope='global'`
30
+
31
+ ---
32
+
33
+ ## Schema compatibility map
34
+
35
+ Current releases do **not** require Redis. The hot cache is SQLite `:memory:`.
36
+ Older versions below 0.5.0 used Redis, so the table keeps that history explicit.
37
+
38
+ | hypermem version | Main DB schema | Library DB schema | Min Node | External cache |
39
+ |---|---|---|---|---|
40
+ | 0.8.0 | v10 | v19 | 22.0.0 | none, SQLite `:memory:` hot cache |
41
+ | 0.7.0 | v7 | v13 | 22.0.0 | none, SQLite `:memory:` hot cache |
42
+ | 0.6.0 | v6 | v12 | 22.0.0 | none, SQLite `:memory:` hot cache |
43
+ | 0.5.0 | v6 | v12 | 22.0.0 | transition release, SQLite `:memory:` hot cache |
44
+ | 0.4.0 | v5 | v10 | 20.0.0 | Redis 6.0.0 |
45
+
46
+ Schema versions are importable for programmatic checks:
47
+ ```ts
48
+ import { SCHEMA_COMPAT, HYPERMEM_COMPAT_VERSION } from 'hypermem';
49
+ // HYPERMEM_COMPAT_VERSION = '0.8.0'
50
+ // SCHEMA_COMPAT = { compatVersion: '0.8.0', mainSchema: 10, librarySchema: 19 }
51
+ ```
52
+
53
+ If your DBs are behind, run:
54
+ ```bash
55
+ node scripts/migrate-legacy-sessions.mjs --apply
56
+ ```