hippo-memory 0.24.0 → 0.24.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/README.md CHANGED
@@ -60,6 +60,18 @@ hippo recall "data pipeline issues" --budget 2000
60
60
 
61
61
  ---
62
62
 
63
+ ### What's new in v0.24.2
64
+
65
+ - **One daily runner per machine, not one task per project.** `hippo init` now registers each workspace and installs a machine-level `hippo daily-runner` job that sweeps every registered Hippo project at 6:15am.
66
+ - **OpenClaw session-end autosleep no longer blocks shutdown.** The native OpenClaw plugin now detaches `hippo sleep` on `session_end` when `autoSleep` is enabled.
67
+ - **Retrieval and refresh are now documented separately across tools.** Query-time recall still uses local plus global memory, while session-end hooks and the daily runner handle consolidation on their own paths.
68
+
69
+ ### What's new in v0.24.1
70
+
71
+ - **Conflict detection stops over-weighting shared tags.** `feedback` / `policy` tags no longer make unrelated memories look contradictory on the next `hippo sleep`.
72
+ - **Reworded contradictions still get caught.** Hippo keeps pairs like `API auth must be enabled in prod` / `Disable API auth in prod` while dropping the false positives that triggered the review.
73
+ - **Broader regression coverage.** This release adds tests for the exact false-positive examples from the migrated store plus extra polarity cases like `must` vs `should not` and `available` vs `missing`.
74
+
63
75
  ### What's new in v0.24.0
64
76
 
65
77
  - **Codex session-end memory is now automatic on install/update.** Hippo no longer tells Codex users to fix `PATH` by hand. The published package now attempts to wrap the detected `codex` launcher during install or upgrade, and it can self-heal on later Hippo commands if Codex shows up afterward.
@@ -88,7 +100,7 @@ hippo setup # picks up the new SessionEnd capture hook
88
100
 
89
101
  ### What's new in v0.21.0
90
102
 
91
- - **`hippo setup` — one command, every tool.** Detects Claude Code, OpenCode, OpenClaw, Codex, Cursor, and Pi on your machine and installs all available SessionEnd + SessionStart hooks in one pass. Idempotent — safe to re-run.
103
+ - **`hippo setup` — one command, every tool.** Detects Claude Code, OpenCode, OpenClaw, Codex, Cursor, and Pi on your machine and installs all available SessionEnd + SessionStart hooks in one pass. It also repairs the machine-level daily runner. Idempotent — safe to re-run.
92
104
  - **OpenCode hooks.** SessionEnd + SessionStart install into `~/.config/opencode/opencode.json` (OpenCode added Claude-Code-compatible hooks in Jan 2026).
93
105
  - **You actually see consolidation output now.** New `SessionStart` hook prints the previous session's `hippo sleep` output between banners on the next startup. Previously, SessionEnd output was invisible because the TUI was tearing down when it ran.
94
106
 
@@ -204,8 +216,8 @@ hippo init
204
216
  ```
205
217
 
206
218
  If you have a `CLAUDE.md`, it patches it. `AGENTS.md` for Codex/OpenClaw/OpenCode. `.cursorrules` for Cursor. For Codex, Hippo also wraps the detected launcher in place so `/exit` can consolidate memory without a manual PATH step. No manual `hook install` needed. Your agent starts using Hippo on its next session.
207
-
208
- It also sets up a daily cron job (6:15am) that runs `hippo learn --git` and `hippo sleep` automatically. Memories get captured from your commits and consolidated every day without you thinking about it.
219
+
220
+ 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.
209
221
 
210
222
  To skip: `hippo init --no-hooks --no-schedule`
211
223
 
@@ -476,9 +488,9 @@ Agents can see at a glance what's established fact vs. a pattern worth questioni
476
488
 
477
489
  Memories unretrieved for 30+ days are automatically marked `stale` during the next `hippo sleep`. If one gets recalled again, Hippo wakes it back up to `observed` so it can earn trust again instead of staying permanently stale.
478
490
 
479
- ### Conflict tracking
480
-
481
- Hippo now detects obvious contradictions between overlapping memories and keeps them visible instead of silently letting both masquerade as truth.
491
+ ### Conflict tracking
492
+
493
+ Hippo detects obvious contradictions between overlapping memories and keeps them visible instead of silently letting both masquerade as truth. Shared tags alone do not count; the statements themselves need to overlap in content.
482
494
 
483
495
  ```bash
484
496
  hippo sleep # refreshes open conflicts
@@ -653,9 +665,10 @@ hippo watch "npm run build"
653
665
  | `hippo embed` | Embed all memories for semantic search |
654
666
  | `hippo embed --status` | Show embedding coverage |
655
667
  | `hippo watch "<command>"` | Run command, auto-learn from failures |
656
- | `hippo learn --git` | Scan recent git commits for lessons |
657
- | `hippo learn --git --days <n>` | Scan N days back (default: 7) |
658
- | `hippo learn --git --repos <paths>` | Scan multiple repos (comma-separated) |
668
+ | `hippo learn --git` | Scan recent git commits for lessons |
669
+ | `hippo learn --git --days <n>` | Scan N days back (default: 7) |
670
+ | `hippo learn --git --repos <paths>` | Scan multiple repos (comma-separated) |
671
+ | `hippo daily-runner` | Sweep registered workspaces and run daily learn+sleep |
659
672
  | `hippo conflicts` | List detected open memory conflicts |
660
673
  | `hippo conflicts --json` | Output conflicts as JSON |
661
674
  | `hippo resolve <id>` | Show both conflicting memories for comparison |
@@ -703,7 +716,7 @@ hippo watch "npm run build"
703
716
  | Claude Code | `CLAUDE.md` or `.claude/settings.json` | `CLAUDE.md` + `SessionStart`/`SessionEnd` hooks in `settings.json` |
704
717
  | Codex | `AGENTS.md` or `.codex` | `AGENTS.md` + automatic in-place Codex launcher wrapper |
705
718
  | Cursor | `.cursorrules` or `.cursor/rules` | `.cursorrules` |
706
- | OpenClaw | `.openclaw` or `AGENTS.md` | `AGENTS.md` |
719
+ | OpenClaw | `.openclaw` or `AGENTS.md` | native OpenClaw plugin or `AGENTS.md` |
707
720
  | OpenCode | `.opencode/` or `opencode.json` | `AGENTS.md` |
708
721
 
709
722
  No extra commands needed. Just `hippo init` and your agent knows about Hippo.
@@ -769,8 +782,14 @@ Exposes tools: `hippo_recall`, `hippo_remember`, `hippo_outcome`, `hippo_context
769
782
 
770
783
  ### OpenClaw Plugin
771
784
 
772
- Native plugin with auto-context injection, workspace-aware memory lookup, and
773
- tool hooks for auto-learn / auto-sleep.
785
+ Native plugin with auto-context injection, workspace-aware memory lookup, and
786
+ tool hooks for auto-learn / auto-sleep. When `autoSleep` is enabled, the
787
+ OpenClaw plugin now launches `hippo sleep` in a detached background worker at
788
+ session end so the live session can exit immediately.
789
+
790
+ Query-time retrieval still uses the active workspace store plus the shared
791
+ global store. Daily consolidation comes from the machine-level runner that
792
+ `hippo init` / `hippo setup` installs.
774
793
 
775
794
  ```bash
776
795
  openclaw plugins install hippo-memory
package/dist/cli.d.ts CHANGED
@@ -19,6 +19,7 @@
19
19
  * hippo embed [--status]
20
20
  * hippo watch "<command>"
21
21
  * hippo learn --git [--days <n>] [--repos <paths>]
22
+ * hippo daily-runner
22
23
  * hippo promote <id>
23
24
  * hippo sync
24
25
  * hippo decide "<decision>" [--context "<why>"] [--supersedes <id>]
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
package/dist/cli.js CHANGED
@@ -19,6 +19,7 @@
19
19
  * hippo embed [--status]
20
20
  * hippo watch "<command>"
21
21
  * hippo learn --git [--days <n>] [--repos <paths>]
22
+ * hippo daily-runner
22
23
  * hippo promote <id>
23
24
  * hippo sync
24
25
  * hippo decide "<decision>" [--context "<why>"] [--supersedes <id>]
@@ -27,7 +28,7 @@
27
28
  import * as path from 'path';
28
29
  import * as fs from 'fs';
29
30
  import * as os from 'os';
30
- import { execSync, spawn } from 'child_process';
31
+ import { execFileSync, execSync, spawn } from 'child_process';
31
32
  import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths, detectInstalledTools, defaultSleepLogPath, ensureCodexWrapperInstalled, installCodexWrapper, uninstallCodexWrapper, resolveCodexSessionTranscript, resolveCodexWrapperPaths, } from './hooks.js';
32
33
  import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, applyOutcome, computeSchemaFit, Layer, DECISION_HALF_LIFE_DAYS, } from './memory.js';
33
34
  import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, } from './store.js';
@@ -42,6 +43,7 @@ import { captureError, extractLessons, deduplicateLesson, runWatched, fetchGitLo
42
43
  import { extractInvalidationTarget, invalidateMatching } from './invalidation.js';
43
44
  import { extractPathTags } from './path-context.js';
44
45
  import { getGlobalRoot, initGlobal, promoteToGlobal, shareMemory, listPeers, autoShare, transferScore, searchBothHybrid, syncGlobalToLocal, } from './shared.js';
46
+ import { DAILY_TASK_NAME, buildDailyRunnerCommand, listRegisteredWorkspaces, registerWorkspace, runDailyMaintenance, } from './scheduler.js';
45
47
  import { importChatGPT, importClaude, importCursor, importGenericFile, importMarkdown, } from './importers.js';
46
48
  import { cmdCapture } from './capture.js';
47
49
  import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
@@ -160,6 +162,7 @@ function cmdInitScan(scanDir, flags) {
160
162
  if (!alreadyExists) {
161
163
  initStore(repoHippo);
162
164
  }
165
+ registerWorkspace(globalRoot, repo);
163
166
  // Learn from git history
164
167
  let added = 0;
165
168
  if (!flags['no-learn'] && isGitRepo(repo)) {
@@ -173,6 +176,9 @@ function cmdInitScan(scanDir, flags) {
173
176
  }
174
177
  console.log(`\n${repos.length} repositories, ${totalLessons} new lessons learned.`);
175
178
  console.log(`Global store: ${globalRoot}`);
179
+ if (!flags['no-schedule']) {
180
+ setupDailySchedule(globalRoot);
181
+ }
176
182
  console.log(`\nRun \`hippo sleep\` in any project to consolidate and auto-share to global.`);
177
183
  }
178
184
  function cmdInit(hippoRoot, flags) {
@@ -202,13 +208,15 @@ function cmdInit(hippoRoot, flags) {
202
208
  console.log(' Directories: buffer/ episodic/ semantic/ conflicts/');
203
209
  console.log(' Files: hippo.db index.json stats.json');
204
210
  }
211
+ const globalRoot = getGlobalRoot();
212
+ registerWorkspace(globalRoot, path.dirname(hippoRoot));
205
213
  // Auto-detect and install hooks (unless --no-hooks)
206
214
  if (!flags['no-hooks']) {
207
215
  autoInstallHooks(alreadyExists);
208
216
  }
209
217
  // Auto-setup daily schedule (unless --no-schedule)
210
218
  if (!flags['no-schedule'] && !flags['global']) {
211
- setupDailySchedule(hippoRoot);
219
+ setupDailySchedule(globalRoot);
212
220
  }
213
221
  // Seed with git history on first init (unless --no-learn)
214
222
  if (!alreadyExists && !flags['no-learn'] && !flags['global']) {
@@ -301,22 +309,24 @@ function autoInstallHooks(quiet) {
301
309
  }
302
310
  }
303
311
  /**
304
- * Set up a daily cron job for hippo learn + sleep.
312
+ * Set up a machine-level daily runner that sweeps all registered Hippo
313
+ * workspaces.
305
314
  * Linux/macOS: writes to user crontab.
306
315
  * Windows: creates a scheduled task.
307
316
  * Skips if already installed.
308
317
  */
309
- function setupDailySchedule(hippoRoot) {
310
- const projectDir = path.resolve(path.dirname(hippoRoot));
318
+ function setupDailySchedule(globalRoot) {
319
+ const runnerDir = path.resolve(globalRoot);
311
320
  // Reject paths with characters that could break shell/crontab quoting
312
321
  // (backslash is normal on Windows, only dangerous in Unix shell/crontab)
313
322
  const unsafeChars = process.platform === 'win32' ? /["`$%\n\r]/ : /["`$\n\r\\]/;
314
- if (unsafeChars.test(projectDir)) {
315
- console.log(` Skipping schedule: project path contains unsafe characters.`);
323
+ if (unsafeChars.test(runnerDir)) {
324
+ console.log(` Skipping schedule: runner path contains unsafe characters.`);
316
325
  return;
317
326
  }
318
327
  const isWindows = process.platform === 'win32';
319
- const taskName = `hippo-daily-${path.basename(projectDir)}`.replace(/[^a-zA-Z0-9_-]/g, '-');
328
+ const taskName = DAILY_TASK_NAME;
329
+ const cmd = buildDailyRunnerCommand(runnerDir);
320
330
  if (isWindows) {
321
331
  // Check if task already exists
322
332
  try {
@@ -328,14 +338,13 @@ function setupDailySchedule(hippoRoot) {
328
338
  catch {
329
339
  // Task doesn't exist, create it
330
340
  }
331
- const cmd = `cd /d "${projectDir}" && hippo learn --git --days 1 && hippo sleep`;
332
341
  try {
333
342
  execSync(`schtasks /create /tn "${taskName}" /tr "cmd /c ${cmd.replace(/"/g, '""')}" /sc daily /st 06:15 /f`, { stdio: 'pipe' });
334
- console.log(` Scheduled daily learn+sleep (6:15am) via Task Scheduler: ${taskName}`);
343
+ console.log(` Scheduled machine-level daily runner (6:15am) via Task Scheduler: ${taskName}`);
335
344
  }
336
345
  catch {
337
346
  // No admin rights or schtasks unavailable, fall back to printing instructions
338
- console.log(` To schedule daily learn+sleep, run:`);
347
+ console.log(` To schedule the machine-level daily runner, run:`);
339
348
  console.log(` schtasks /create /tn "${taskName}" /tr "cmd /c ${cmd}" /sc daily /st 06:15`);
340
349
  }
341
350
  }
@@ -347,14 +356,14 @@ function setupDailySchedule(hippoRoot) {
347
356
  if (existing.includes(marker)) {
348
357
  return; // already scheduled
349
358
  }
350
- const cronLine = `15 6 * * * cd "${projectDir}" && hippo learn --git --days 1 && hippo sleep ${marker}`;
359
+ const cronLine = `15 6 * * * ${cmd} ${marker}`;
351
360
  const newCrontab = existing.trimEnd() + '\n' + cronLine + '\n';
352
361
  execSync('crontab -', { input: newCrontab, stdio: ['pipe', 'pipe', 'pipe'] });
353
- console.log(` Scheduled daily learn+sleep (6:15am) via crontab`);
362
+ console.log(` Scheduled machine-level daily runner (6:15am) via crontab`);
354
363
  }
355
364
  catch {
356
- const cronLine = `15 6 * * * cd "${projectDir}" && hippo learn --git --days 1 && hippo sleep`;
357
- console.log(` To schedule daily learn+sleep, add to crontab (crontab -e):`);
365
+ const cronLine = `15 6 * * * ${cmd}`;
366
+ console.log(` To schedule the machine-level daily runner, add to crontab (crontab -e):`);
358
367
  console.log(` ${cronLine}`);
359
368
  }
360
369
  }
@@ -2391,6 +2400,7 @@ function cmdSetup(flags) {
2391
2400
  const dryRun = Boolean(flags['dry-run']);
2392
2401
  const forceAll = Boolean(flags['all']);
2393
2402
  const tools = detectInstalledTools();
2403
+ const globalRoot = getGlobalRoot();
2394
2404
  console.log('Hippo setup -- configuring SessionEnd + SessionStart hooks');
2395
2405
  console.log('');
2396
2406
  const jsonTools = tools.filter((t) => t.kind === 'json-hook' && (t.detected || forceAll));
@@ -2464,9 +2474,46 @@ function cmdSetup(flags) {
2464
2474
  console.log(` ${tool.name.padEnd(14)} ${tool.notes}`);
2465
2475
  }
2466
2476
  }
2477
+ if (!flags['no-schedule']) {
2478
+ console.log('');
2479
+ if (dryRun) {
2480
+ console.log(`[dry-run] would install the machine-level daily runner around ${globalRoot}`);
2481
+ }
2482
+ else {
2483
+ setupDailySchedule(globalRoot);
2484
+ }
2485
+ }
2467
2486
  console.log('');
2468
2487
  console.log('Done. Restart your AI tool to activate the hooks.');
2469
2488
  }
2489
+ function cmdDailyRunner() {
2490
+ const globalRoot = getGlobalRoot();
2491
+ const workspaces = listRegisteredWorkspaces(globalRoot);
2492
+ if (workspaces.length === 0) {
2493
+ console.log('No registered Hippo workspaces found. Run `hippo init` inside a project first.');
2494
+ return;
2495
+ }
2496
+ console.log(`Running daily maintenance across ${workspaces.length} registered workspace${workspaces.length === 1 ? '' : 's'}...`);
2497
+ let processed = 0;
2498
+ let failed = 0;
2499
+ runDailyMaintenance(workspaces, (cwd, args) => {
2500
+ try {
2501
+ execFileSync(process.execPath, [process.argv[1], ...args], {
2502
+ cwd,
2503
+ stdio: 'inherit',
2504
+ windowsHide: true,
2505
+ });
2506
+ if (args[0] === 'sleep')
2507
+ processed++;
2508
+ }
2509
+ catch (err) {
2510
+ failed++;
2511
+ const action = args.join(' ');
2512
+ console.error(`[hippo] daily-runner failed in ${cwd} during \`${action}\`: ${err.message}`);
2513
+ }
2514
+ });
2515
+ console.log(`Daily maintenance complete: ${processed} workspace${processed === 1 ? '' : 's'} processed, ${failed} command failure${failed === 1 ? '' : 's'}.`);
2516
+ }
2470
2517
  // JSON-hook install/uninstall lives in ./hooks.ts so tests can import it
2471
2518
  // without running the CLI main(). Backwards-compatible wrappers below keep
2472
2519
  // older call sites working.
@@ -2555,13 +2602,13 @@ Hippo - biologically-inspired memory system for AI agents
2555
2602
  Usage: hippo <command> [options]
2556
2603
 
2557
2604
  Commands:
2558
- init Create .hippo/ structure in current directory
2559
- --scan [dir] Find all git repos under dir (default: ~) and init each
2560
- --days <n> Days of git history to seed (default: 365 for --scan, 30 for single)
2561
- --global Init the global store ($HIPPO_HOME or ~/.hippo/)
2562
- --no-hooks Skip auto-detecting and installing agent hooks
2563
- --no-schedule Skip auto-creating daily learn+sleep cron job
2564
- --no-learn Skip seeding memories from git history
2605
+ init Create .hippo/ structure in current directory
2606
+ --scan [dir] Find all git repos under dir (default: ~) and init each
2607
+ --days <n> Days of git history to seed (default: 365 for --scan, 30 for single)
2608
+ --global Init the global store ($HIPPO_HOME or ~/.hippo/)
2609
+ --no-hooks Skip auto-detecting and installing agent hooks
2610
+ --no-schedule Skip auto-creating the machine-level daily runner
2611
+ --no-learn Skip seeding memories from git history
2565
2612
  remember <text> Store a memory
2566
2613
  --tag <tag> Add a tag (repeatable)
2567
2614
  --error Tag as error (boosts retention)
@@ -2574,16 +2621,17 @@ Commands:
2574
2621
  --budget <n> Token budget (default: 4000)
2575
2622
  --json Output as JSON
2576
2623
  --why Show match reasons and source annotations
2577
- context Smart context injection for AI agents
2624
+ context Smart context injection for AI agents
2578
2625
  --auto Auto-detect task from git state
2579
2626
  --budget <n> Token budget (default: 1500)
2580
2627
  --format <fmt> Output format: markdown (default) or json
2581
2628
  --framing <mode> Framing: observe (default), suggest, assert
2582
- sleep Run consolidation pass (auto-learns + dedup + auto-shares)
2583
- --dry-run Preview without writing
2584
- --no-learn Skip auto git-learn before consolidation
2585
- --no-share Skip auto-sharing to global store
2586
- dedup Remove duplicate memories (keeps stronger copy)
2629
+ sleep Run consolidation pass (auto-learns + dedup + auto-shares)
2630
+ --dry-run Preview without writing
2631
+ --no-learn Skip auto git-learn before consolidation
2632
+ --no-share Skip auto-sharing to global store
2633
+ daily-runner Sweep registered workspaces and run daily learn+sleep
2634
+ dedup Remove duplicate memories (keeps stronger copy)
2587
2635
  --dry-run Preview without removing
2588
2636
  --threshold <n> Overlap threshold 0-1 (default: 0.7)
2589
2637
  status Show memory health stats
@@ -2675,10 +2723,11 @@ Commands:
2675
2723
  --log-file <path> Tee output to a log file (paired with 'hippo last-sleep')
2676
2724
  --dry-run Preview without writing
2677
2725
  --global Write to global store ($HIPPO_HOME or ~/.hippo/)
2678
- setup One-shot: detect installed AI tools and install all
2679
- available SessionEnd+SessionStart hooks
2680
- --all Install for every JSON-hook tool, even if not detected
2681
- --dry-run Show what would be installed without writing
2726
+ setup One-shot: detect installed AI tools and install all
2727
+ available SessionEnd+SessionStart hooks
2728
+ --all Install for every JSON-hook tool, even if not detected
2729
+ --dry-run Show what would be installed without writing
2730
+ --no-schedule Skip installing or repairing the daily runner
2682
2731
  last-sleep Print the last 'hippo sleep --log-file' output and clear it
2683
2732
  --path <p> Log path (default: ~/.hippo/logs/last-sleep.log)
2684
2733
  --keep Print without clearing
@@ -2844,6 +2893,9 @@ async function main() {
2844
2893
  case 'setup':
2845
2894
  cmdSetup(flags);
2846
2895
  break;
2896
+ case 'daily-runner':
2897
+ cmdDailyRunner();
2898
+ break;
2847
2899
  case 'embed':
2848
2900
  await cmdEmbed(hippoRoot, flags);
2849
2901
  break;