hippo-memory 1.52.2 → 1.52.4

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
@@ -1,13 +1,15 @@
1
- # 🦛 Hippo
1
+ # 🦛 Hippo: memory for AI agents that learns what is wrong
2
2
 
3
3
  **Hippo learns what is wrong and stops repeating it.** Good memory is knowing what to forget: what turned out wrong, what got replaced, what nobody used.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/hippo-memory)](https://npmjs.com/package/hippo-memory)
6
- [![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
6
+ [![npm downloads](https://img.shields.io/npm/dm/hippo-memory)](https://npmjs.com/package/hippo-memory)
7
+ [![CI](https://github.com/kitfunso/hippo-memory/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/kitfunso/hippo-memory/actions/workflows/ci.yml)
8
+ [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/kitfunso/hippo-memory/blob/master/LICENSE)
7
9
  [![website](https://img.shields.io/badge/website-hippo--memory.com-7c3aed)](https://hippo-memory.com)
8
10
 
9
11
  <p align="center">
10
- <img src="./assets/hippo-init.svg" alt="hippo init --scan ~ — initializing memory across all repos" width="720">
12
+ <img src="https://raw.githubusercontent.com/kitfunso/hippo-memory/master/assets/hippo-init.svg" alt="hippo init --scan ~ initializing memory across all repos" width="720">
11
13
  </p>
12
14
 
13
15
  A memory layer for AI agents. Mark a memory wrong and it stops coming back. A newer fact replaces the old one. Memories you use get stronger. Provenance on every memory. SQLite under the hood, zero runtime deps, works with every CLI agent you have.
@@ -27,6 +29,8 @@ Storage: SQLite backbone with markdown mirrors. Git-trackable, human-reada
27
29
  Dependencies: Zero runtime deps. Node.js 22.16+. Optional embeddings: bring-your-own local Transformers.js (`npm i @huggingface/transformers`, or legacy `@xenova/transformers`) or an opt-in API embedder (OpenAI/Voyage/Cohere). Nothing is auto-installed.
28
30
  ```
29
31
 
32
+ **Contents:** [Why](#why-this-exists) · [Receipts](#receipts) · [Quick start](#quick-start) · [Agent setup](#framework-integrations) · [MCP server](#mcp-server) · [How it works](#how-it-works) · [Features](#key-features) · [CLI](#cli-reference) · [Comparison](#comparison) · [Benchmarks](#benchmarks) · [FAQ](#faq) · [Contributing](#contributing)
33
+
30
34
  ---
31
35
 
32
36
  ## Why this exists
@@ -112,7 +116,7 @@ hippo init
112
116
  # Auto-installed claude-code hook in CLAUDE.md
113
117
  ```
114
118
 
115
- If you have a `CLAUDE.md`, it patches it. `AGENTS.md` for Codex/OpenClaw/OpenCode. `.cursorrules` for Cursor. Your agent starts using Hippo on its next session. For Codex session capture, Hippo wraps the codex launcher only when you explicitly opt in with `hippo hook install codex` (init prints the command when it detects Codex; undo anytime with `hippo hook uninstall codex`).
119
+ If you have a `CLAUDE.md`, it patches it. `AGENTS.md` for Codex/OpenClaw/OpenCode/Pi. `.cursorrules` for Cursor. Your agent starts using Hippo on its next session. For Codex session capture, Hippo wraps the codex launcher only when you explicitly opt in with `hippo hook install codex` (init prints the command when it detects Codex; undo anytime with `hippo hook uninstall codex`).
116
120
 
117
121
  It also registers the current project in Hippo's workspace registry and installs one machine-level daily runner (6:15am). That runner sweeps every registered workspace, runs `hippo learn --git --days 1`, then `hippo sleep`. You get strict daily consolidation without creating one OS task per project.
118
122
 
@@ -628,7 +632,7 @@ hippo watch "npm run build"
628
632
  | `hippo dormant [<query>]` | List faded memories sleep kept instead of deleting |
629
633
  | `hippo dormant restore <id>` | Bring a dormant memory back to active memory |
630
634
  | `hippo dormant forget <id>` | Delete a dormant memory permanently |
631
- | `hippo doctor [--json]` | Check the install: Node, store, schema, sleep, agent hooks; each problem names its fix |
635
+ | `hippo doctor [--json]` | Check the install: Node, store, schema, sleep, agent hooks; each problem names its fix. It never changes `hippo.db`, though SQLite may leave empty `hippo.db-wal` and `hippo.db-shm` files beside it |
632
636
  | `hippo support-bundle [--out <file>] [--include-logs]` | Write a redacted JSON file for a support ticket: versions, doctor checks, config, store counts and log names, never memory text; `--include-logs` adds each log's last 200 lines, which can quote it |
633
637
  | `hippo tokens [--days n]` | Estimated tokens of memory text handed to agents, per surface, and what skipping unchanged hook blocks saved |
634
638
  | `hippo failures [--days n]` | Failed tool calls the capture-error hook saw, by outcome, and how many errors first happened in another session |
@@ -696,10 +700,11 @@ On `heartbeat`, `block`, `review` and `complete`, a given `--run` is checked aga
696
700
  | Framework | Detected by | Patches |
697
701
  |-----------|------------|---------|
698
702
  | Claude Code | `CLAUDE.md` or `.claude/settings.json` | `CLAUDE.md` + `SessionStart`/`SessionEnd` hooks in `settings.json` |
699
- | Codex | `AGENTS.md` or `.codex` | `AGENTS.md` + automatic in-place Codex launcher wrapper |
703
+ | Codex | `AGENTS.md` or `.codex` | `AGENTS.md`; session capture is opt-in with `hippo hook install codex`, which wraps the Codex launcher |
700
704
  | Cursor | `.cursorrules` or `.cursor/rules` | `.cursorrules` |
701
705
  | OpenClaw | `.openclaw` or `AGENTS.md` | native OpenClaw plugin or `AGENTS.md` |
702
706
  | OpenCode | `.opencode/` or `opencode.json` | `AGENTS.md` + TS plugin at `~/.config/opencode/plugins/hippo.ts` (subscribes to `session.idle` + `session.created`) |
707
+ | Pi | `.pi` or `.pi/agent` | `AGENTS.md`; copy the [Pi extension](https://github.com/kitfunso/hippo-memory/tree/master/extensions/pi-extension) for session hooks |
703
708
 
704
709
  No extra commands needed. Just `hippo init` and your agent knows about Hippo.
705
710
 
@@ -843,7 +848,7 @@ The AI-memory category matured fast in 2026. Hippo's specific take — bio-decay
843
848
  | Lossless compression | No | Yes (AAAK, 30x) | No | No | No | No | No | No | No | No |
844
849
  | Cross-tool import (ChatGPT/Claude/Cursor) | Yes | No | No | No | Partial (data sources) | ? | No | Partial (28 data sources) | No (Git ops) | Partial (mRAG: PDFs/images/URLs) |
845
850
  | Auto-hook install | Yes | No | No | No | No | No | No | No | No | No |
846
- | MCP server | Yes | Yes | No | No | Yes (stdio + HTTP/OAuth) | Partial (managed) | Yes (via Letta Code) | Yes (first-party Claude/LangGraph) | Yes | ? |
851
+ | MCP server | Yes | Yes | Yes (hosted, needs an account) | Yes | Yes (stdio + HTTP/OAuth) | Partial (managed) | Yes (via Letta Code) | Yes (first-party Claude/LangGraph) | Yes | ? |
847
852
  | Zero runtime deps | Yes | No (ChromaDB) | No | No | No (PGLite or PG+pgvector) | No (managed service) | No (Python deps) | No (Python deps) | Yes (single Rust binary) | No (managed + OSS) |
848
853
  | LongMemEval (best published) | 98.0% local / 99.8% voyage R@5 (s_cleaned, per-haystack)\* | 96.6% raw / 100% reranked R@5 | ~49-85% R@5 | N/A | 97.6-97.9% R@5 (s_cleaned\*) | N/A (LoCoMo 80.3%) | N/A | N/A | 88.78% overall accuracy w/ reader\*\* | 83.00% overall\*\* (LoCoMo 93.05%, HaluMem 93.04%) |
849
854
  | Git-friendly | Yes | No | No | Yes | Yes | No | No | No | Yes (Git is the model) | ? |
@@ -950,6 +955,54 @@ node run.mjs --adapter all
950
955
 
951
956
  ---
952
957
 
958
+ ## FAQ
959
+
960
+ ### How do I give Claude Code memory between sessions?
961
+
962
+ Run `npm install -g hippo-memory`, then `hippo init` in the project, or `hippo init --scan ~` for every repo on the machine. If the project has a `CLAUDE.md`, init adds a short block telling Claude to run `hippo context --auto` when a session starts. It also adds hooks to Claude Code's settings that keep your pinned memories in context, save a task snapshot before compaction, and run `hippo sleep` when the session ends. The [Claude Code plugin](https://github.com/kitfunso/hippo-memory/tree/master/extensions/claude-code-plugin) is the alternative to these hooks; use one, not both.
963
+
964
+ ### How do I give Cursor memory between sessions?
965
+
966
+ `hippo init` adds its instructions to `.cursorrules` if the project has one, and the [MCP server](#mcp-server) gives Cursor's agent tools to recall and store memories once you add `hippo mcp` to `.cursor/mcp.json`. `hippo import --cursor .cursorrules` turns your existing rules into memories.
967
+
968
+ ### How do I give Codex memory across sessions?
969
+
970
+ `hippo init` adds its instructions to your `AGENTS.md`, which Codex reads before it starts work. Capturing Codex sessions is opt-in: `hippo hook install codex` wraps the Codex launcher, and `hippo hook uninstall codex` removes the wrapper.
971
+
972
+ ### Which agents does hippo work with?
973
+
974
+ `hippo init` detects Claude Code, Codex, Cursor, OpenClaw, OpenCode and Pi, and wires itself into each one's instruction file, hooks or plugin. It only patches instruction files that already exist. Any MCP client can use the [MCP server](#mcp-server), and other tools can call the CLI or the HTTP API that `hippo serve` starts.
975
+
976
+ ### Can I use hippo as an MCP memory server?
977
+
978
+ Yes. `hippo mcp` runs the server over stdio, and `npx -y hippo-memory mcp` runs it without a global install. Add it to the MCP config of Claude Desktop, Cursor, Windsurf, Cline or any other client (example [above](#mcp-server)); in Claude Code, run `claude mcp add hippo-memory -- hippo mcp`. The agent gets tools such as `hippo_recall`, `hippo_remember` and `hippo_outcome`.
979
+
980
+ ### How is hippo different from mem0?
981
+
982
+ mem0 uses a language model to extract memories, OpenAI by default in its open-source library, and memories stored through its hosted MCP server live in your Mem0 account ([mem0 docs](https://docs.mem0.ai/platform/mem0-mcp), checked 2026-09-28). Hippo stores memories in SQLite on your machine, needs no account and no model, and `hippo init` wires it into the coding agents it finds. mem0's platform and hippo both mark an older fact superseded when a newer one replaces it. Hippo also lets you mark a recalled memory wrong with `hippo outcome --bad`, and it drops out of the top results.
983
+
984
+ ### Do I still need CLAUDE.md?
985
+
986
+ Yes, for short standing rules such as build commands, code style and things never to do. Claude Code loads `CLAUDE.md` and its auto memory into every session, and its [memory docs](https://code.claude.com/docs/en/memory) say that when two rules contradict each other, Claude may pick one arbitrarily. Hippo holds the lessons that pile up, recalls the ones that match the task, and retires the ones marked wrong or replaced. `hippo init` adds its block to `CLAUDE.md`, and `hippo import --claude CLAUDE.md` turns existing notes into memories.
987
+
988
+ ### What happens when a memory turns out to be wrong?
989
+
990
+ Mark it, and it stops coming back. `hippo outcome --bad` weakens the memories from the last recall, `hippo supersede <id> "<new fact>"` replaces one with a newer version, and `hippo reject <id> --reason "<why>"` stops that value from returning at all. On the synthetic E1 test, where every mark is correct, plain BM25 plus the outcome mark cut how often a marked-bad memory stayed in the top five from 71.9% to 0.0%. Real marks are noisier, because `--bad` marks the whole recall batch.
991
+
992
+ ### Where does hippo keep my data?
993
+
994
+ On your machine, in SQLite: `.hippo/hippo.db` in each project, plus a global store in `~/.hippo/` for lessons shared across projects, with markdown mirrors you can read and commit. Recall makes no network call by default. Text goes to an outside provider only through features that use one: an API embedder, the Jev or LLM reranker, `hippo refine`, and the fact extraction `hippo sleep` runs through Anthropic's API whenever `ANTHROPIC_API_KEY` is set in its environment. To turn that last one off, set `{"extraction":{"enabled":false}}` in `.hippo/config.json`.
995
+
996
+ ### What does hippo cost?
997
+
998
+ Nothing. Hippo is MIT-licensed and needs no account or API key. Optional features that call an outside provider bill through it: the Jev reranker costs about 0.0004 USD a recall, and API embedders and sleep's fact extraction bill your own keys. Memory text handed to your agent uses context tokens, and `hippo tokens` shows how many.
999
+
1000
+ ### Has hippo been shown to make agents better at their work?
1001
+
1002
+ Not yet. The published numbers measure retrieval: whether the right memory comes back, and whether a memory marked wrong stays out of the results. The paired test that runs real agent sessions with and without hippo has not had a scored run yet. Every measurement, including failed runs and one retracted claim, is indexed in [docs/evals](https://github.com/kitfunso/hippo-memory/blob/master/docs/evals/README.md).
1003
+
1004
+ ---
1005
+
953
1006
  ## Contributing
954
1007
 
955
1008
  Issues and PRs welcome. Before contributing, run `hippo status` in the repo root to see the project's own memory.
@@ -963,6 +1016,12 @@ The interesting problems:
963
1016
  - **MemPalace-style spatial organization.** Could spatial structure (wings/halls/rooms) improve hippo's semantic layer?
964
1017
  - **AAAK-style compression for semantic memories.** Lossless token compression for context injection.
965
1018
 
1019
+ ## Open source and commercial
1020
+
1021
+ Hippo is open core, and the line is drawn by who pays. Everything an individual developer or a self-hosted team needs is in this repository under MIT: the CLI, the MCP server, hooks, connectors, the dashboard, tenants, API keys, roles, per-key scope grants, the audit log and zero-touch memory. Code published here stays MIT and stays here.
1022
+
1023
+ A commercial edition for larger companies ships as a separate package under a commercial licence from KITFUNSO LTD. It adds SSO (OIDC and SAML sign-in), SCIM, an org admin view, the pilot report and telemetry join, SIEM export of the audit log, offline licence keys, hosted SaaS, and support with an SLA. Pull requests for those features belong there, not here; see [CONTRIBUTING.md](CONTRIBUTING.md).
1024
+
966
1025
  ## License
967
1026
 
968
1027
  MIT
@@ -38,6 +38,6 @@ export declare function detectSecret(entry: {
38
38
  * CS1 pre-compact snapshot fields — can scrub it in place instead.
39
39
  */
40
40
  export declare function redactSecrets(text: string): string;
41
- /** Stricter redaction for text that leaves the machine: no co-occurrence guard, plus Bearer headers and JWTs. */
41
+ /** Stricter redaction for text that leaves the machine: no co-occurrence guard, plus Bearer and Basic auth headers and JWTs. */
42
42
  export declare function redactSecretsStrict(text: string): string;
43
43
  //# sourceMappingURL=secret-detect.d.ts.map
@@ -28,6 +28,13 @@ const SECRET_PATTERNS = [
28
28
  { name: 'slack-token', re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/ },
29
29
  { name: 'stripe-key', re: /\b[sr]k_(?:live|test)_[A-Za-z0-9]{16,}\b/ },
30
30
  { name: 'google-api-key', re: /\bAIza[0-9A-Za-z_-]{35}\b/ },
31
+ // hk_ is hippo's own API key (src/auth.ts); npm, Hugging Face, GitLab and Slack webhook shapes follow gitleaks' rules.
32
+ { name: 'hippo-api-key', re: /\bhk_[a-z2-7]{24}\.[a-z2-7]{32}\b/ },
33
+ { name: 'npm-token', re: /\bnpm_[A-Za-z0-9]{36}\b/ },
34
+ { name: 'huggingface-token', re: /\bhf_[A-Za-z]{34}\b/ },
35
+ { name: 'gitlab-token', re: /\bglpat-[\w-]{20,}/ },
36
+ { name: 'google-oauth-token', re: /\bya29\.[\w-]{20,}/ },
37
+ { name: 'slack-webhook', re: /\bhooks\.slack\.com\/(?:services|workflows|triggers)\/[A-Za-z0-9+/]{43,56}/ },
31
38
  { name: 'private-key-block', re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
32
39
  // sk-... (OpenAI/Anthropic style) and sk_<vendor>_... shapes. Both require
33
40
  // a key-ish noun somewhere in the content (co-occurrence guard) so prose
@@ -51,6 +58,7 @@ const CO_OCCURRENCE_GUARDED = new Set(['sk-style-key', 'sk-underscore-key']);
51
58
  // redactSecretsStrict-only: too noisy for whole-entry memory scanning, worth hiding once text leaves the machine.
52
59
  const STRICT_ONLY_PATTERNS = [
53
60
  /\bbearer\s+[A-Za-z0-9._~+/-]{16,}=*/gi,
61
+ /\bauthorization["']?\s*[:=]\s*["']?basic\s+[A-Za-z0-9+/]{8,}={0,2}/gi,
54
62
  /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]*/g,
55
63
  ];
56
64
  /**
@@ -82,7 +90,7 @@ export function detectSecret(entry) {
82
90
  export function redactSecrets(text) {
83
91
  return redactText(text, false);
84
92
  }
85
- /** Stricter redaction for text that leaves the machine: no co-occurrence guard, plus Bearer headers and JWTs. */
93
+ /** Stricter redaction for text that leaves the machine: no co-occurrence guard, plus Bearer and Basic auth headers and JWTs. */
86
94
  export function redactSecretsStrict(text) {
87
95
  return redactText(text, true);
88
96
  }
@@ -1,7 +1,8 @@
1
- /** `hippo support-bundle`: one redacted JSON snapshot for a support ticket. Read-only; never touches memory content. */
1
+ /** `hippo support-bundle`: one redacted JSON snapshot for a support ticket. Read-only (SQLite may leave empty -wal and -shm files); never touches memory content. */
2
2
  import * as fs from 'fs';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
+ import { pathToFileURL } from 'url';
5
6
  import { findHippoStoreDir, isGlobalStoreRoot, realpathOrResolve } from './project-identity.js';
6
7
  import { getGlobalRoot } from './shared.js';
7
8
  import { isInitialized } from './store.js';
@@ -189,18 +190,11 @@ function nativeRealPathKey(p) {
189
190
  return null;
190
191
  }
191
192
  }
192
- // Longest first: where one home form nests in another (a test home under the real one), swap the longer.
193
- function collectHomeForms(home, probes) {
194
- const forms = new Set();
195
- const add = (s) => { if (s.length >= 3)
196
- forms.add(s); };
193
+ function collectHomeAliases(home, probes) {
194
+ const aliases = new Set();
197
195
  for (const base of [home, realpathOrResolve(home), os.homedir(), realpathOrResolve(os.homedir())]) {
198
- add(base);
199
- add(path.resolve(base));
200
- if (process.platform === 'win32') {
201
- add(base.replace(/\\/g, '/'));
202
- add(path.resolve(base).replace(/\\/g, '/'));
203
- }
196
+ aliases.add(base);
197
+ aliases.add(path.resolve(base));
204
198
  }
205
199
  // The alias (e.g. a short-name temp root) can sit above the probe itself, so walk ancestors and
206
200
  // compare native realpaths rather than checking each probe alone.
@@ -209,34 +203,56 @@ function collectHomeForms(home, probes) {
209
203
  let dir = path.resolve(probe);
210
204
  for (;;) {
211
205
  const key = nativeRealPathKey(dir);
212
- if (key !== null && homeKeys.has(key)) {
213
- add(dir);
214
- if (process.platform === 'win32')
215
- add(dir.replace(/\\/g, '/'));
216
- }
206
+ if (key !== null && homeKeys.has(key))
207
+ aliases.add(dir);
217
208
  const parent = path.dirname(dir);
218
209
  if (parent === dir)
219
210
  break;
220
211
  dir = parent;
221
212
  }
222
213
  }
223
- const seen = new Set();
224
- const deduped = [];
225
- for (const f of [...forms].sort((a, b) => b.length - a.length)) {
226
- const key = process.platform === 'win32' ? f.toLowerCase() : f;
227
- if (seen.has(key))
228
- continue;
229
- seen.add(key);
230
- deduped.push(f);
231
- }
232
- return deduped;
214
+ return aliases;
215
+ }
216
+ // Node's own encoding, so it matches file URLs and ESM stack frames; a drive path's URL gains a leading slash.
217
+ function fileUrlPath(p) {
218
+ const pathname = pathToFileURL(p).pathname;
219
+ return /^\/[A-Za-z]:/.test(pathname) ? pathname.slice(1) : pathname;
220
+ }
221
+ // A home turns up as a path, as a file URL's percent-encoded path, and as a Claude Code project folder name.
222
+ const HOME_SPELLINGS = [
223
+ (p) => p,
224
+ fileUrlPath,
225
+ (p) => p.replace(/[^A-Za-z0-9]/g, '-'),
226
+ ];
227
+ // A Windows separator can be written \, / or JSON-escaped (\\ in a log line quoting JSON), so any of them matches.
228
+ function spellingPattern(spelling) {
229
+ if (process.platform !== 'win32')
230
+ return escapeRegExp(spelling);
231
+ return spelling.split(/[\\/]+/).map(escapeRegExp).join('(?:\\\\+|/)');
233
232
  }
234
- function replaceHomeForms(text, forms) {
235
- if (forms.length === 0)
236
- return text;
237
- // Stop only before another name character: "<home> now" and "<home>." swap, a longer user name ("<home>ty") does not.
238
- const re = new RegExp(`(?:${forms.map(escapeRegExp).join('|')})(?![\\p{L}\\p{N}_])`, process.platform === 'win32' ? 'giu' : 'gu');
239
- return text.replace(re, '~');
233
+ /** One pattern for every alias of the home in every spelling, or null when none is long enough to swap safely. */
234
+ function buildHomePattern(home, probes) {
235
+ const spellings = new Set();
236
+ for (const alias of collectHomeAliases(home, probes)) {
237
+ for (const spell of HOME_SPELLINGS) {
238
+ const s = spell(alias);
239
+ if (s.length >= 3)
240
+ spellings.add(s);
241
+ }
242
+ }
243
+ // Longest first: where one home nests in another (a test home under the real one), swap the longer.
244
+ const patterns = new Map();
245
+ for (const s of [...spellings].sort((a, b) => b.length - a.length)) {
246
+ const pattern = spellingPattern(s);
247
+ const key = process.platform === 'win32' ? pattern.toLowerCase() : pattern;
248
+ if (!patterns.has(key))
249
+ patterns.set(key, pattern);
250
+ }
251
+ if (patterns.size === 0)
252
+ return null;
253
+ // Whole names only: "<home> now" and "<home>." swap; "<home>ty", or "web-app" for a home of /app, do not.
254
+ const alternatives = [...patterns.values()].join('|');
255
+ return new RegExp(`(?<![\\p{L}\\p{N}_])(?:${alternatives})(?![\\p{L}\\p{N}_])`, process.platform === 'win32' ? 'giu' : 'gu');
240
256
  }
241
257
  const URL_RE = /\b[a-z][a-z0-9+.-]*:\/\/[^\s"'<>]+/gi;
242
258
  function redactUrlsInString(text) {
@@ -255,37 +271,40 @@ function redactUrlsInString(text) {
255
271
  }
256
272
  });
257
273
  }
258
- function redactString(text, homeForms) {
259
- return replaceHomeForms(redactSecretsStrict(redactUrlsInString(text)), homeForms);
274
+ function redactString(text, homePattern) {
275
+ const scrubbed = redactSecretsStrict(redactUrlsInString(text));
276
+ return homePattern === null ? scrubbed : scrubbed.replace(homePattern, '~');
260
277
  }
261
- function redactStrings(value, homeForms) {
278
+ function redactStrings(value, homePattern) {
262
279
  if (isJsonString(value))
263
- return redactString(value, homeForms);
280
+ return redactString(value, homePattern);
264
281
  if (Array.isArray(value))
265
- return value.map((v) => redactStrings(v, homeForms));
282
+ return value.map((v) => redactStrings(v, homePattern));
266
283
  if (isJsonObject(value)) {
267
284
  const out = {};
268
285
  for (const [k, v] of Object.entries(value))
269
- out[k] = redactStrings(v, homeForms);
286
+ out[k] = redactStrings(v, homePattern);
270
287
  return out;
271
288
  }
272
289
  return value;
273
290
  }
274
291
  /** Read-only: builds one redacted support-ticket snapshot. Never reads a memory content column. */
275
292
  export function buildSupportBundle(opts) {
293
+ // Before doctor: its read-only open leaves an empty -wal that the store's file list would then report.
294
+ const stores = buildStores(opts);
276
295
  const bundle = {
277
296
  format: 'hippo-support-bundle/1',
278
297
  createdAt: opts.now.toISOString(),
279
298
  hippo: opts.version,
280
299
  runtime: buildRuntime(),
281
300
  doctor: JSON.parse(JSON.stringify(runDoctor(opts))),
282
- stores: buildStores(opts),
301
+ stores,
283
302
  env: listSetEnvNames(),
284
303
  logs: buildLogsSection(opts),
285
304
  };
286
305
  // Store paths found from the working folder arrive resolved; the global root arrives as HIPPO_HOME was typed, and log lines
287
306
  // often quote the temp folder, which usually sits under the home.
288
- const redacted = redactStrings(bundle, collectHomeForms(opts.home, [getGlobalRoot(), os.tmpdir()]));
307
+ const redacted = redactStrings(bundle, buildHomePattern(opts.home, [getGlobalRoot(), os.tmpdir()]));
289
308
  // SAFETY: the value built above is always a JsonObject; redactStrings preserves object shape.
290
309
  return redacted;
291
310
  }
package/dist/version.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
17
17
  * any packager that drops .json files.
18
18
  */
19
- export declare const PACKAGE_VERSION = "1.52.2";
19
+ export declare const PACKAGE_VERSION = "1.52.4";
20
20
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
21
21
  export declare function compareSemver(a: string, b: string): number;
22
22
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -16,7 +16,7 @@
16
16
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
17
17
  * any packager that drops .json files.
18
18
  */
19
- export const PACKAGE_VERSION = '1.52.2';
19
+ export const PACKAGE_VERSION = '1.52.4';
20
20
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
21
21
  export function compareSemver(a, b) {
22
22
  const parse = (v) => {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
- "description": "Biologically-inspired memory for AI agents. Decay by default, retrieval strengthening, sleep consolidation.",
5
- "version": "1.52.2",
4
+ "description": "Memory for AI agents that learns what is wrong and stops repeating it. Injects context at session start and captures errors.",
5
+ "version": "1.52.4",
6
6
 
7
7
  "configSchema": {
8
8
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippo-memory",
3
- "version": "1.52.2",
3
+ "version": "1.52.4",
4
4
  "type": "module",
5
5
  "description": "Hippo Memory plugin for OpenClaw - biologically-inspired agent memory",
6
6
  "main": "index.ts",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
- "description": "Biologically-inspired memory for AI agents. Decay by default, retrieval strengthening, sleep consolidation.",
5
- "version": "1.52.2",
4
+ "description": "Memory for AI agents that learns what is wrong and stops repeating it. Injects context at session start and captures errors.",
5
+ "version": "1.52.4",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hippo-memory",
3
- "version": "1.52.2",
4
- "description": "Biologically-inspired memory for AI agents. Zero runtime deps, SQLite, MCP server, and an opt-in hosted TypeSafe Jev reranker. Decay, retrieval strengthening, consolidation.",
3
+ "version": "1.52.4",
4
+ "description": "Memory for AI agents that learns what is wrong and stops repeating it. MCP server and hooks for Claude Code, Codex and Cursor. SQLite, zero runtime deps.",
5
5
  "mcpName": "io.github.kitfunso/hippo-memory",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -46,26 +46,34 @@
46
46
  },
47
47
  "keywords": [
48
48
  "memory",
49
- "ai",
50
- "agents",
51
- "llm",
52
- "claude",
53
- "context",
54
- "recall",
55
- "episodic",
56
- "semantic",
57
- "embeddings",
58
- "hybrid-search",
59
49
  "agent-memory",
50
+ "ai-memory",
51
+ "llm-memory",
60
52
  "long-term-memory",
53
+ "persistent-memory",
54
+ "memory-layer",
61
55
  "mcp",
56
+ "mcp-server",
62
57
  "model-context-protocol",
63
58
  "claude-code",
59
+ "claude",
60
+ "codex",
61
+ "cursor",
62
+ "openclaw",
63
+ "opencode",
64
+ "ai-agents",
65
+ "coding-agent",
66
+ "agents",
67
+ "llm",
64
68
  "rag",
65
69
  "retrieval",
70
+ "hybrid-search",
71
+ "embeddings",
66
72
  "sqlite",
73
+ "local-first",
67
74
  "cli",
68
- "typescript"
75
+ "context",
76
+ "recall"
69
77
  ],
70
78
  "author": "Keith",
71
79
  "license": "MIT",