hippo-memory 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +8 -0
  2. package/dist/cli.js +83 -8
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.d.ts +7 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +9 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/consolidate.d.ts +5 -1
  9. package/dist/consolidate.d.ts.map +1 -1
  10. package/dist/consolidate.js +64 -10
  11. package/dist/consolidate.js.map +1 -1
  12. package/dist/dag.d.ts +20 -0
  13. package/dist/dag.d.ts.map +1 -0
  14. package/dist/dag.js +104 -0
  15. package/dist/dag.js.map +1 -0
  16. package/dist/db.d.ts.map +1 -1
  17. package/dist/db.js +23 -1
  18. package/dist/db.js.map +1 -1
  19. package/dist/extract.d.ts +14 -0
  20. package/dist/extract.d.ts.map +1 -0
  21. package/dist/extract.js +87 -0
  22. package/dist/extract.js.map +1 -0
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/memory.d.ts +6 -0
  28. package/dist/memory.d.ts.map +1 -1
  29. package/dist/memory.js +3 -0
  30. package/dist/memory.js.map +1 -1
  31. package/dist/multihop.d.ts +11 -0
  32. package/dist/multihop.d.ts.map +1 -0
  33. package/dist/multihop.js +32 -0
  34. package/dist/multihop.js.map +1 -0
  35. package/dist/search.d.ts +9 -0
  36. package/dist/search.d.ts.map +1 -1
  37. package/dist/search.js +140 -7
  38. package/dist/search.js.map +1 -1
  39. package/dist/store.d.ts.map +1 -1
  40. package/dist/store.js +14 -3
  41. package/dist/store.js.map +1 -1
  42. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  43. package/extensions/openclaw-plugin/package.json +1 -1
  44. package/openclaw.plugin.json +1 -1
  45. package/package.json +1 -1
package/README.md CHANGED
@@ -60,6 +60,14 @@ hippo recall "data pipeline issues" --budget 2000
60
60
 
61
61
  ---
62
62
 
63
+ ### What's new in v0.33.0
64
+
65
+ - **Fact extraction at sleep time.** `hippo sleep` now extracts standalone facts from episodic memories via LLM, stored as semantic-layer entries that score 1.3x higher and auto-deduplicate against their raw source in search results.
66
+ - **DAG summarization.** Extracted facts cluster by entity similarity into summary nodes. When a summary matches your query, its children drill down into results automatically.
67
+ - **Multi-hop retrieval.** `hippo recall --multihop` chains two search passes via entity tags discovered in the first pass, finding connections that single-pass search misses.
68
+ - **`hippo dag --stats`** shows how your memory is organized across DAG levels.
69
+ - **Performance fix.** Temporal scoring refactored from O(N^2) to O(N), eliminating stack overflow risk on large stores.
70
+
63
71
  ### What's new in v0.32.0
64
72
 
65
73
  - **Correction without deletion.** `hippo supersede <old-id> "<new content>"` links the old memory as historical truth and creates a successor. Default recall shows only current beliefs; the old one stays in the store so you can audit what changed and when.
package/dist/cli.js CHANGED
@@ -53,6 +53,7 @@ import { auditMemories } from './audit.js';
53
53
  import { runEval, bootstrapCorpus, compareSummaries } from './eval.js';
54
54
  import { refineStore } from './refine-llm.js';
55
55
  import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
56
+ import { multihopSearch } from './multihop.js';
56
57
  // ---------------------------------------------------------------------------
57
58
  // Helpers
58
59
  // ---------------------------------------------------------------------------
@@ -377,7 +378,7 @@ function setupDailySchedule(globalRoot) {
377
378
  }
378
379
  }
379
380
  }
380
- function cmdRemember(hippoRoot, text, flags) {
381
+ async function cmdRemember(hippoRoot, text, flags) {
381
382
  const useGlobal = Boolean(flags['global']);
382
383
  const targetRoot = useGlobal ? getGlobalRoot() : hippoRoot;
383
384
  if (useGlobal) {
@@ -437,6 +438,28 @@ function cmdRemember(hippoRoot, text, flags) {
437
438
  // Silently ignore embedding errors
438
439
  });
439
440
  }
441
+ const config = loadConfig(targetRoot);
442
+ const shouldExtract = flags['extract'] || config.extraction.enabled === true;
443
+ const apiKey = process.env.ANTHROPIC_API_KEY ?? '';
444
+ if (shouldExtract && apiKey) {
445
+ try {
446
+ const { extractFacts, storeExtractedFacts } = await import('./extract.js');
447
+ const facts = await extractFacts(entry.content, {
448
+ apiKey,
449
+ model: config.extraction.model,
450
+ });
451
+ if (facts.length > 0) {
452
+ storeExtractedFacts(targetRoot, entry, facts);
453
+ console.error(` extracted ${facts.length} fact(s)`);
454
+ }
455
+ }
456
+ catch {
457
+ // Extraction is best-effort — never block remember
458
+ }
459
+ }
460
+ else if (shouldExtract && !apiKey) {
461
+ console.error(' (extraction skipped: ANTHROPIC_API_KEY not set)');
462
+ }
440
463
  }
441
464
  function cmdSupersede(hippoRoot, oldId, newContent, flags) {
442
465
  requireInit(hippoRoot);
@@ -535,8 +558,19 @@ async function cmdRecall(hippoRoot, query, flags) {
535
558
  : undefined;
536
559
  const recallExplicitScope = flags['scope'] !== undefined ? String(flags['scope']).trim() : null;
537
560
  const recallActiveScope = recallExplicitScope || detectScope();
561
+ const useMultihop = flags['multihop'] === true || config.multihop.enabled;
538
562
  let results;
539
- if (usePhysics && !hasGlobal) {
563
+ if (useMultihop) {
564
+ const allEntries = [...localEntries, ...globalEntries];
565
+ results = multihopSearch(query, allEntries, {
566
+ budget,
567
+ hippoRoot,
568
+ minResults,
569
+ includeSuperseded,
570
+ asOf,
571
+ });
572
+ }
573
+ else if (usePhysics && !hasGlobal) {
540
574
  results = await physicsSearch(query, localEntries, {
541
575
  budget,
542
576
  hippoRoot,
@@ -1296,7 +1330,7 @@ function cmdDedup(hippoRoot, flags) {
1296
1330
  console.log(` ... and ${result.pairs.length - 15} more (run with --dry-run to see all)`);
1297
1331
  }
1298
1332
  }
1299
- function cmdSleep(hippoRoot, flags) {
1333
+ async function cmdSleep(hippoRoot, flags) {
1300
1334
  // Tee stdout/stderr to a log file when --log-file is set. The SessionEnd
1301
1335
  // hook uses this so the output is captured somewhere the SessionStart hook
1302
1336
  // can re-display it next time the agent UI starts.
@@ -1335,7 +1369,7 @@ function cmdSleep(hippoRoot, flags) {
1335
1369
  }
1336
1370
  }
1337
1371
  try {
1338
- cmdSleepCore(hippoRoot, flags);
1372
+ await cmdSleepCore(hippoRoot, flags);
1339
1373
  if (logFile)
1340
1374
  console.log('[hippo] sleep complete');
1341
1375
  }
@@ -1349,7 +1383,7 @@ function cmdSleep(hippoRoot, flags) {
1349
1383
  restoreStdout();
1350
1384
  }
1351
1385
  }
1352
- function cmdSleepCore(hippoRoot, flags) {
1386
+ async function cmdSleepCore(hippoRoot, flags) {
1353
1387
  requireInit(hippoRoot);
1354
1388
  // Auto-learn from git before consolidating (unless --no-learn)
1355
1389
  if (!flags['no-learn']) {
@@ -1366,7 +1400,7 @@ function cmdSleepCore(hippoRoot, flags) {
1366
1400
  }
1367
1401
  const dryRun = Boolean(flags['dry-run']);
1368
1402
  console.log(`Running consolidation${dryRun ? ' (dry run)' : ''}...`);
1369
- const result = consolidate(hippoRoot, { dryRun });
1403
+ const result = await consolidate(hippoRoot, { dryRun });
1370
1404
  console.log(`\nResults:`);
1371
1405
  console.log(` Active memories: ${result.decayed}`);
1372
1406
  console.log(` Removed (decayed): ${result.removed}`);
@@ -3374,6 +3408,44 @@ function cmdWm(hippoRoot, args, flags) {
3374
3408
  console.error('Usage: hippo wm <push|read|clear|flush>');
3375
3409
  process.exit(1);
3376
3410
  }
3411
+ function cmdDag(hippoRoot, flags) {
3412
+ requireInit(hippoRoot);
3413
+ const entries = loadAllEntries(hippoRoot);
3414
+ const isStats = flags['stats'] === true;
3415
+ const byLevel = new Map();
3416
+ let unlinked = 0;
3417
+ for (const entry of entries) {
3418
+ const level = entry.dag_level ?? 0;
3419
+ byLevel.set(level, (byLevel.get(level) ?? 0) + 1);
3420
+ if (level === 1 && !entry.dag_parent_id)
3421
+ unlinked++;
3422
+ }
3423
+ if (isStats) {
3424
+ console.log('DAG Structure:');
3425
+ console.log(` Level 3 (entity profiles): ${byLevel.get(3) ?? 0}`);
3426
+ console.log(` Level 2 (topic summaries): ${byLevel.get(2) ?? 0}`);
3427
+ console.log(` Level 1 (extracted facts): ${byLevel.get(1) ?? 0}`);
3428
+ console.log(` Level 0 (raw memories): ${byLevel.get(0) ?? 0}`);
3429
+ console.log(` Unlinked facts: ${unlinked}`);
3430
+ return;
3431
+ }
3432
+ // Tree view: show summaries and their children
3433
+ const summaries = entries.filter((e) => e.dag_level === 2);
3434
+ if (summaries.length === 0) {
3435
+ console.log('No DAG summaries yet. Run `hippo sleep` with ANTHROPIC_API_KEY set.');
3436
+ return;
3437
+ }
3438
+ for (const summary of summaries) {
3439
+ const summaryTags = summary.tags.filter((t) => t !== 'dag-summary').join(', ');
3440
+ console.log(`\nšŸ“Œ ${summary.content.slice(0, 80)}`);
3441
+ if (summaryTags)
3442
+ console.log(` [${summaryTags}]`);
3443
+ const children = entries.filter((e) => e.dag_parent_id === summary.id);
3444
+ for (const child of children) {
3445
+ console.log(` └─ ${child.content.slice(0, 70)}`);
3446
+ }
3447
+ }
3448
+ }
3377
3449
  function printUsage() {
3378
3450
  console.log(`
3379
3451
  Hippo - biologically-inspired memory system for AI agents
@@ -3638,7 +3710,7 @@ async function main() {
3638
3710
  console.error('Memory content too short (minimum 3 characters).');
3639
3711
  process.exit(1);
3640
3712
  }
3641
- cmdRemember(hippoRoot, text, flags);
3713
+ await cmdRemember(hippoRoot, text, flags);
3642
3714
  break;
3643
3715
  }
3644
3716
  case 'recall': {
@@ -3691,7 +3763,7 @@ async function main() {
3691
3763
  await cmdRefine(hippoRoot, flags);
3692
3764
  break;
3693
3765
  case 'sleep':
3694
- cmdSleep(hippoRoot, flags);
3766
+ await cmdSleep(hippoRoot, flags);
3695
3767
  break;
3696
3768
  case 'last-sleep':
3697
3769
  cmdLastSleep(flags);
@@ -3711,6 +3783,9 @@ async function main() {
3711
3783
  case 'dedup':
3712
3784
  cmdDedup(hippoRoot, flags);
3713
3785
  break;
3786
+ case 'dag':
3787
+ cmdDag(hippoRoot, flags);
3788
+ break;
3714
3789
  case 'audit': {
3715
3790
  requireInit(hippoRoot);
3716
3791
  const entries = loadAllEntries(hippoRoot);