hippo-memory 0.10.0 → 0.11.1
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 +97 -15
- package/dist/cli.js +93 -12
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/consolidate.d.ts +1 -0
- package/dist/consolidate.d.ts.map +1 -1
- package/dist/consolidate.js +61 -2
- package/dist/consolidate.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +19 -1
- package/dist/db.js.map +1 -1
- package/dist/embeddings.d.ts +1 -0
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +31 -1
- package/dist/embeddings.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/memory.d.ts +26 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +44 -7
- package/dist/memory.js.map +1 -1
- package/dist/physics-config.d.ts +37 -0
- package/dist/physics-config.d.ts.map +1 -0
- package/dist/physics-config.js +26 -0
- package/dist/physics-config.js.map +1 -0
- package/dist/physics-state.d.ts +43 -0
- package/dist/physics-state.d.ts.map +1 -0
- package/dist/physics-state.js +158 -0
- package/dist/physics-state.js.map +1 -0
- package/dist/physics.d.ts +115 -0
- package/dist/physics.d.ts.map +1 -0
- package/dist/physics.js +354 -0
- package/dist/physics.js.map +1 -0
- package/dist/search.d.ts +13 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +105 -0
- package/dist/search.js.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +14 -5
- package/dist/store.js.map +1 -1
- package/extensions/openclaw-plugin/README.md +11 -1
- package/extensions/openclaw-plugin/index.ts +62 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -43,10 +43,22 @@ hippo recall "data pipeline issues" --budget 2000
|
|
|
43
43
|
|
|
44
44
|
That's it. You have a memory system.
|
|
45
45
|
|
|
46
|
+
### What's new in v0.11.1
|
|
47
|
+
|
|
48
|
+
- **OpenClaw error capture filtering.** The `autoLearn` hook now applies three filters before storing tool errors: a noise pattern filter for known transient errors, per-session rate limiting (max 5), and per-session deduplication. Prevents memory pollution from infrastructure noise.
|
|
49
|
+
|
|
50
|
+
### What's new in v0.11.0
|
|
51
|
+
|
|
52
|
+
- **Reward-proportional decay.** Outcome feedback now modulates decay rate continuously instead of fixed half-life deltas. Memories with consistent positive outcomes decay up to 1.5x slower; consistent negatives decay up to 2x faster. Mixed outcomes converge toward neutral. Inspired by R-STDP in spiking neural networks. `hippo inspect` now shows cumulative outcome counts and the computed reward factor.
|
|
53
|
+
- **Public benchmarks.** Two benchmarks in `benchmarks/`: a [Sequential Learning Benchmark](benchmarks/sequential-learning/) (50 tasks, 10 traps, measures agent improvement over time) and a [LongMemEval integration](benchmarks/longmemeval/) (industry-standard 500-question retrieval benchmark, R@5=74.0% with BM25 only). The sequential learning benchmark is unique: no other public benchmark tests whether memory systems produce learning curves.
|
|
54
|
+
|
|
46
55
|
### What's new in v0.10.0
|
|
47
56
|
|
|
48
57
|
- **Active invalidation.** `hippo learn --git` detects migration and breaking-change commits and actively weakens memories referencing the old pattern. Manual invalidation via `hippo invalidate "REST API" --reason "migrated to GraphQL"`.
|
|
49
58
|
- **Architectural decisions.** `hippo decide` stores one-off decisions with 90-day half-life and verified confidence. Supports `--context` for reasoning and `--supersedes` to chain decisions when the architecture evolves.
|
|
59
|
+
- **Path-based memory triggers.** Memories auto-tagged with `path:<segment>` from your working directory. Recall boosts memories from the same location (up to 1.3x). Working in `src/api/`? API-related memories surface first.
|
|
60
|
+
- **OpenCode integration.** `hippo hook install opencode` patches AGENTS.md. Auto-detected during `hippo init`. Integration guide with MCP config and skill for progressive discovery.
|
|
61
|
+
- **`hippo export`** outputs all memories as JSON or markdown.
|
|
50
62
|
- **Decision recall boost.** 1.2x scoring multiplier for decision-tagged memories so they surface despite low retrieval frequency.
|
|
51
63
|
|
|
52
64
|
### What's new in v0.9.1
|
|
@@ -421,14 +433,15 @@ hippo recall "why is the gold model broken"
|
|
|
421
433
|
|
|
422
434
|
hippo outcome --good
|
|
423
435
|
# Applied positive outcome to 3 memories
|
|
424
|
-
#
|
|
436
|
+
# reward factor increases, decay slows
|
|
425
437
|
|
|
426
438
|
hippo outcome --bad
|
|
427
439
|
# Applied negative outcome to 3 memories
|
|
428
|
-
#
|
|
429
|
-
# irrelevant memories decay faster
|
|
440
|
+
# reward factor decreases, decay accelerates
|
|
430
441
|
```
|
|
431
442
|
|
|
443
|
+
Outcomes are cumulative. A memory with 5 positive outcomes and 0 negative has a reward factor of ~1.42, making its effective half-life 42% longer. A memory with 0 positive and 3 negative has a factor of ~0.63, decaying nearly twice as fast. Mixed outcomes converge toward neutral (1.0).
|
|
444
|
+
|
|
432
445
|
---
|
|
433
446
|
|
|
434
447
|
### Token budgets
|
|
@@ -687,34 +700,100 @@ The 7 mechanisms in full: [PLAN.md#core-principles](PLAN.md#core-principles)
|
|
|
687
700
|
|
|
688
701
|
For how these mechanisms connect to LLM training, continual learning, and open research problems: **[RESEARCH.md](RESEARCH.md)**
|
|
689
702
|
|
|
690
|
-
**
|
|
703
|
+
**Why does reward modulate decay?** In spiking neural networks, reward-modulated STDP strengthens synapses that contribute to positive outcomes and weakens those that don't. Hippo's reward-proportional decay (v0.11.0) implements this: memories with consistent positive outcomes decay slower, negatives decay faster, with no fixed deltas. Inspired by [MH-FLOCKE](https://github.com/MarcHesse/mhflocke)'s R-STDP architecture for quadruped locomotion, where the same mechanism produces stable learning with 11.6x lower variance than PPO.
|
|
704
|
+
|
|
705
|
+
**Prior art in agent memory simulation.** The idea that human-like memory produces human-like behavior as an emergent property was explored in IEEE research from 2010-2011 ([5952114](https://ieeexplore.ieee.org/document/5952114), [5548405](https://ieeexplore.ieee.org/document/5548405), [5953964](https://ieeexplore.ieee.org/document/5953964)). Walking between rooms and forgetting why you went there doesn't need direct simulation; it emerges naturally from a memory system with capacity limits and decay. Hippo's design follows the same principle: implement the mechanisms, and the behavior follows.
|
|
706
|
+
|
|
707
|
+
**Related work:** [HippoRAG](https://arxiv.org/abs/2405.14831) (Gutierrez et al., 2024) applies hippocampal indexing to RAG via knowledge graphs. [MemPalace](https://github.com/milla-jovovich/mempalace) (Sigman & Jovovich, 2026) organizes memory spatially (wings/halls/rooms) with AAAK compression, achieving 100% on [LongMemEval](https://arxiv.org/abs/2410.10813). [MH-FLOCKE](https://github.com/MarcHesse/mhflocke) (Hesse, 2026) uses spiking neurons with R-STDP for embodied cognition. Each system tackles a different facet: HippoRAG optimizes retrieval quality, MemPalace optimizes retrieval organization, MH-FLOCKE optimizes embodied learning, and Hippo optimizes memory lifecycle.
|
|
691
708
|
|
|
692
709
|
---
|
|
693
710
|
|
|
694
711
|
## Comparison
|
|
695
712
|
|
|
696
|
-
| Feature | Hippo | Mem0 | Basic Memory |
|
|
697
|
-
|
|
713
|
+
| Feature | Hippo | MemPalace | Mem0 | Basic Memory |
|
|
714
|
+
|---------|-------|-----------|------|-------------|
|
|
698
715
|
| Decay by default | Yes | No | No | No |
|
|
699
716
|
| Retrieval strengthening | Yes | No | No | No |
|
|
700
|
-
|
|
|
717
|
+
| Reward-proportional decay | Yes | No | No | No |
|
|
718
|
+
| Hybrid search (BM25 + embeddings) | Yes | Embeddings + spatial | Embeddings only | No |
|
|
701
719
|
| Schema acceleration | Yes | No | No | No |
|
|
702
720
|
| Conflict detection + resolution | Yes | No | No | No |
|
|
703
721
|
| Multi-agent shared memory | Yes | No | No | No |
|
|
704
722
|
| Transfer scoring | Yes | No | No | No |
|
|
705
723
|
| Outcome tracking | Yes | No | No | No |
|
|
706
724
|
| Confidence tiers | Yes | No | No | No |
|
|
725
|
+
| Spatial organization | No | Yes (wings/halls/rooms) | No | No |
|
|
726
|
+
| Lossless compression | No | Yes (AAAK, 30x) | No | No |
|
|
707
727
|
| Cross-tool import | Yes | No | No | No |
|
|
708
|
-
| Conversation capture | Yes | No | No | No |
|
|
709
728
|
| Auto-hook install | Yes | No | No | No |
|
|
710
|
-
| MCP server | Yes |
|
|
711
|
-
|
|
|
712
|
-
|
|
|
713
|
-
|
|
|
714
|
-
|
|
|
715
|
-
|
|
729
|
+
| MCP server | Yes | Yes | No | No |
|
|
730
|
+
| Zero dependencies | Yes | No (ChromaDB) | No | No |
|
|
731
|
+
| LongMemEval R@5 (retrieval) | 74.0% (BM25 only) | 96.6% (raw) / 100% (reranked) | ~49-85% | N/A |
|
|
732
|
+
| Git-friendly | Yes | No | No | Yes |
|
|
733
|
+
| Framework agnostic | Yes | Yes | Partial | Yes |
|
|
734
|
+
|
|
735
|
+
Different tools answer different questions. Mem0 and Basic Memory implement "save everything, search later." MemPalace implements "store everything, organize spatially for retrieval." Hippo implements "forget by default, earn persistence through use." These are complementary approaches: MemPalace's retrieval precision + Hippo's lifecycle management would be stronger than either alone.
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
## Benchmarks
|
|
740
|
+
|
|
741
|
+
Two benchmarks testing two different things. Full details in [`benchmarks/`](benchmarks/).
|
|
742
|
+
|
|
743
|
+
### LongMemEval (retrieval accuracy)
|
|
744
|
+
|
|
745
|
+
[LongMemEval](https://arxiv.org/abs/2410.10813) (ICLR 2025) is the industry-standard benchmark: 500 questions across 5 memory abilities, embedded in 115k+ token chat histories.
|
|
746
|
+
|
|
747
|
+
**Hippo v0.11.0 results (BM25 only, zero dependencies):**
|
|
716
748
|
|
|
717
|
-
|
|
749
|
+
| Metric | Score |
|
|
750
|
+
|--------|-------|
|
|
751
|
+
| Recall@1 | 50.4% |
|
|
752
|
+
| Recall@3 | 66.6% |
|
|
753
|
+
| Recall@5 | 74.0% |
|
|
754
|
+
| Recall@10 | 82.6% |
|
|
755
|
+
| Answer in content@5 | 46.6% |
|
|
756
|
+
|
|
757
|
+
| Question Type | Count | R@5 |
|
|
758
|
+
|---------------|-------|-----|
|
|
759
|
+
| single-session-assistant | 56 | 94.6% |
|
|
760
|
+
| knowledge-update | 78 | 88.5% |
|
|
761
|
+
| temporal-reasoning | 133 | 73.7% |
|
|
762
|
+
| multi-session | 133 | 72.2% |
|
|
763
|
+
| single-session-user | 70 | 65.7% |
|
|
764
|
+
| single-session-preference | 30 | 26.7% |
|
|
765
|
+
|
|
766
|
+
For context: MemPalace scores 96.6% (raw) using ChromaDB embeddings + spatial indexing. Hippo achieves 74.0% using BM25 keyword matching alone with zero runtime dependencies. Adding embeddings via `hippo embed` (optional `@xenova/transformers` peer dep) enables hybrid search and should close the gap.
|
|
767
|
+
|
|
768
|
+
Hippo's strongest categories (knowledge-update 88.5%, single-session-assistant 94.6%) are the ones where keyword overlap between question and stored content is highest. The weakest (preference 26.7%) involves indirect references that need semantic understanding.
|
|
769
|
+
|
|
770
|
+
```bash
|
|
771
|
+
cd benchmarks/longmemeval
|
|
772
|
+
python ingest_direct.py --data data/longmemeval_oracle.json --store-dir ./store
|
|
773
|
+
python retrieve_fast.py --data data/longmemeval_oracle.json --store-dir ./store --output results/retrieval.jsonl
|
|
774
|
+
python evaluate_retrieval.py --retrieval results/retrieval.jsonl --data data/longmemeval_oracle.json
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### Sequential Learning Benchmark (agent improvement over time)
|
|
778
|
+
|
|
779
|
+
No other public benchmark tests whether memory systems produce learning curves. LongMemEval tests retrieval on a fixed corpus. This benchmark tests whether an agent with memory *performs better on task 40 than task 5*.
|
|
780
|
+
|
|
781
|
+
50 tasks, 10 trap categories, each appearing 2-3 times across the sequence.
|
|
782
|
+
|
|
783
|
+
**Hippo v0.11.0 results:**
|
|
784
|
+
|
|
785
|
+
| Condition | Overall | Early | Mid | Late | Learns? |
|
|
786
|
+
|-----------|---------|-------|-----|------|---------|
|
|
787
|
+
| No memory | 100% | 100% | 100% | 100% | No |
|
|
788
|
+
| Static memory | 20% | 33% | 11% | 14% | No |
|
|
789
|
+
| Hippo | 40% | 78% | 22% | 14% | Yes |
|
|
790
|
+
|
|
791
|
+
The hippo agent's trap-hit rate drops from 78% to 14% as it accumulates error memories with 2x half-life. Static pre-loaded memory helps from the start but doesn't improve. Any memory system can run this benchmark by implementing the [adapter interface](benchmarks/sequential-learning/adapters/interface.mjs).
|
|
792
|
+
|
|
793
|
+
```bash
|
|
794
|
+
cd benchmarks/sequential-learning
|
|
795
|
+
node run.mjs --adapter all
|
|
796
|
+
```
|
|
718
797
|
|
|
719
798
|
---
|
|
720
799
|
|
|
@@ -723,10 +802,13 @@ Mem0, Basic Memory, and Claude-Mem all implement "save everything, search later.
|
|
|
723
802
|
Issues and PRs welcome. Before contributing, run `hippo status` in the repo root to see the project's own memory.
|
|
724
803
|
|
|
725
804
|
The interesting problems:
|
|
805
|
+
- **Improve LongMemEval score.** Current R@5 is 74.0% with BM25 only. Adding embeddings (`hippo embed`) and hybrid search should close the gap toward MemPalace's 96.6%.
|
|
726
806
|
- Better consolidation heuristics (LLM-powered merge vs current text overlap)
|
|
727
807
|
- Web UI / dashboard for visualizing decay curves and memory health
|
|
728
808
|
- Optimal decay parameter tuning from real usage data
|
|
729
809
|
- Cross-agent transfer learning evaluation
|
|
810
|
+
- **MemPalace-style spatial organization.** Could spatial structure (wings/halls/rooms) improve hippo's semantic layer?
|
|
811
|
+
- **AAAK-style compression for semantic memories.** Lossless token compression for context injection.
|
|
730
812
|
|
|
731
813
|
## License
|
|
732
814
|
|
package/dist/cli.js
CHANGED
|
@@ -28,11 +28,15 @@ import * as path from 'path';
|
|
|
28
28
|
import * as fs from 'fs';
|
|
29
29
|
import * as os from 'os';
|
|
30
30
|
import { execSync } from 'child_process';
|
|
31
|
-
import { createMemory, calculateStrength, deriveHalfLife, resolveConfidence, applyOutcome, computeSchemaFit, Layer, DECISION_HALF_LIFE_DAYS, } from './memory.js';
|
|
31
|
+
import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, applyOutcome, computeSchemaFit, Layer, DECISION_HALF_LIFE_DAYS, } from './memory.js';
|
|
32
32
|
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';
|
|
33
|
-
import { markRetrieved, estimateTokens, hybridSearch, explainMatch } from './search.js';
|
|
33
|
+
import { markRetrieved, estimateTokens, hybridSearch, physicsSearch, explainMatch } from './search.js';
|
|
34
34
|
import { consolidate } from './consolidate.js';
|
|
35
35
|
import { isEmbeddingAvailable, embedAll, embedMemory, loadEmbeddingIndex, } from './embeddings.js';
|
|
36
|
+
import { loadPhysicsState, resetAllPhysicsState } from './physics-state.js';
|
|
37
|
+
import { computeSystemEnergy, vecNorm } from './physics.js';
|
|
38
|
+
import { loadConfig } from './config.js';
|
|
39
|
+
import { openHippoDb, closeHippoDb } from './db.js';
|
|
36
40
|
import { captureError, extractLessons, deduplicateLesson, runWatched, fetchGitLog, isGitRepo, } from './autolearn.js';
|
|
37
41
|
import { extractInvalidationTarget, invalidateMatching } from './invalidation.js';
|
|
38
42
|
import { extractPathTags } from './path-context.js';
|
|
@@ -297,12 +301,26 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
297
301
|
const limit = parseLimitFlag(flags['limit']);
|
|
298
302
|
const asJson = Boolean(flags['json']);
|
|
299
303
|
const showWhy = Boolean(flags['why']);
|
|
304
|
+
const forcePhysics = Boolean(flags['physics']);
|
|
305
|
+
const forceClassic = Boolean(flags['classic']);
|
|
300
306
|
const globalRoot = getGlobalRoot();
|
|
301
307
|
const localEntries = loadSearchEntries(hippoRoot, query);
|
|
302
308
|
const globalEntries = isInitialized(globalRoot) ? loadSearchEntries(globalRoot, query) : [];
|
|
303
309
|
const hasGlobal = globalEntries.length > 0;
|
|
310
|
+
// Determine search mode: --physics forces physics, --classic forces BM25+cosine,
|
|
311
|
+
// default uses physics if config.physics.enabled is not false
|
|
312
|
+
const config = loadConfig(hippoRoot);
|
|
313
|
+
const usePhysics = forcePhysics
|
|
314
|
+
|| (!forceClassic && config.physics.enabled !== false);
|
|
304
315
|
let results;
|
|
305
|
-
if (hasGlobal) {
|
|
316
|
+
if (usePhysics && !hasGlobal) {
|
|
317
|
+
results = await physicsSearch(query, localEntries, {
|
|
318
|
+
budget,
|
|
319
|
+
hippoRoot,
|
|
320
|
+
physicsConfig: config.physics,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else if (hasGlobal) {
|
|
306
324
|
// Use searchBothHybrid for merged results with embedding support
|
|
307
325
|
results = await searchBothHybrid(query, hippoRoot, globalRoot, { budget });
|
|
308
326
|
}
|
|
@@ -464,8 +482,42 @@ function cmdStatus(hippoRoot) {
|
|
|
464
482
|
console.log(`Embeddings: ${embAvail ? 'available' : 'not installed (BM25 only)'}`);
|
|
465
483
|
if (embAvail) {
|
|
466
484
|
const embIndex = loadEmbeddingIndex(hippoRoot);
|
|
467
|
-
const
|
|
468
|
-
|
|
485
|
+
const activeIds = new Set(entries.map((e) => e.id));
|
|
486
|
+
const activeEmbedded = Object.keys(embIndex).filter((id) => activeIds.has(id)).length;
|
|
487
|
+
const orphaned = Object.keys(embIndex).length - activeEmbedded;
|
|
488
|
+
let line = `Embedded: ${activeEmbedded}/${entries.length} memories`;
|
|
489
|
+
if (orphaned > 0)
|
|
490
|
+
line += ` (${orphaned} orphaned — run \`hippo embed\` to prune)`;
|
|
491
|
+
console.log(line);
|
|
492
|
+
}
|
|
493
|
+
// Physics status
|
|
494
|
+
try {
|
|
495
|
+
const db = openHippoDb(hippoRoot);
|
|
496
|
+
try {
|
|
497
|
+
const physicsMap = loadPhysicsState(db);
|
|
498
|
+
if (physicsMap.size > 0) {
|
|
499
|
+
const particles = Array.from(physicsMap.values());
|
|
500
|
+
const physConfig = loadConfig(hippoRoot);
|
|
501
|
+
const energy = computeSystemEnergy(particles, physConfig.physics.G_memory);
|
|
502
|
+
let sumVelMag = 0;
|
|
503
|
+
let maxVelMag = 0;
|
|
504
|
+
for (const p of particles) {
|
|
505
|
+
const mag = vecNorm(p.velocity);
|
|
506
|
+
sumVelMag += mag;
|
|
507
|
+
if (mag > maxVelMag)
|
|
508
|
+
maxVelMag = mag;
|
|
509
|
+
}
|
|
510
|
+
const avgVelMag = sumVelMag / particles.length;
|
|
511
|
+
console.log('');
|
|
512
|
+
console.log(`Physics: ${particles.length} particles, energy: ${fmt(energy.total, 4)} (KE: ${fmt(energy.kinetic, 4)}, PE: ${fmt(energy.potential, 4)}), avg vel: ${fmt(avgVelMag, 4)}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
finally {
|
|
516
|
+
closeHippoDb(db);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
// Physics table may not exist yet — degrade gracefully
|
|
469
521
|
}
|
|
470
522
|
}
|
|
471
523
|
function cmdOutcome(hippoRoot, flags) {
|
|
@@ -533,7 +585,13 @@ function cmdInspect(hippoRoot, id) {
|
|
|
533
585
|
console.log(`Schema fit: ${entry.schema_fit}`);
|
|
534
586
|
console.log(`Pinned: ${entry.pinned}`);
|
|
535
587
|
console.log(`Tags: ${entry.tags.join(', ') || 'none'}`);
|
|
536
|
-
|
|
588
|
+
const rewardFactor = calculateRewardFactor(entry);
|
|
589
|
+
const pos = entry.outcome_positive ?? 0;
|
|
590
|
+
const neg = entry.outcome_negative ?? 0;
|
|
591
|
+
const outcomeLabel = pos === 0 && neg === 0
|
|
592
|
+
? 'none'
|
|
593
|
+
: `+${pos} / -${neg} (reward factor: ${fmt(rewardFactor)})`;
|
|
594
|
+
console.log(`Outcomes: ${outcomeLabel}`);
|
|
537
595
|
if (entry.conflicts_with.length > 0) {
|
|
538
596
|
console.log(`Conflicts with: ${entry.conflicts_with.join(', ')}`);
|
|
539
597
|
}
|
|
@@ -1021,7 +1079,12 @@ async function cmdContext(hippoRoot, args, flags) {
|
|
|
1021
1079
|
}));
|
|
1022
1080
|
}
|
|
1023
1081
|
else {
|
|
1024
|
-
|
|
1082
|
+
const ctxConfig = loadConfig(hippoRoot);
|
|
1083
|
+
const usePhysicsCtx = ctxConfig.physics?.enabled !== false;
|
|
1084
|
+
const ctxResults = usePhysicsCtx
|
|
1085
|
+
? await physicsSearch(query, localEntries, { budget, hippoRoot, physicsConfig: ctxConfig.physics })
|
|
1086
|
+
: await hybridSearch(query, localEntries, { budget, hippoRoot });
|
|
1087
|
+
results = ctxResults.map((r) => ({
|
|
1025
1088
|
entry: r.entry,
|
|
1026
1089
|
score: r.score,
|
|
1027
1090
|
tokens: r.tokens,
|
|
@@ -1151,11 +1214,29 @@ async function cmdEmbed(hippoRoot, flags) {
|
|
|
1151
1214
|
console.log(' npm install @xenova/transformers');
|
|
1152
1215
|
return;
|
|
1153
1216
|
}
|
|
1217
|
+
if (flags['reset-physics']) {
|
|
1218
|
+
const entries = loadAllEntries(hippoRoot);
|
|
1219
|
+
const embIndex = loadEmbeddingIndex(hippoRoot);
|
|
1220
|
+
const db = openHippoDb(hippoRoot);
|
|
1221
|
+
try {
|
|
1222
|
+
const count = resetAllPhysicsState(db, entries, embIndex);
|
|
1223
|
+
console.log(`Reset physics state: ${count} particles re-initialized from embeddings.`);
|
|
1224
|
+
}
|
|
1225
|
+
finally {
|
|
1226
|
+
closeHippoDb(db);
|
|
1227
|
+
}
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1154
1230
|
if (flags['status']) {
|
|
1155
1231
|
const entries = loadAllEntries(hippoRoot);
|
|
1156
1232
|
const embIndex = loadEmbeddingIndex(hippoRoot);
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1233
|
+
const activeIds = new Set(entries.map((e) => e.id));
|
|
1234
|
+
const activeEmbedded = Object.keys(embIndex).filter((id) => activeIds.has(id)).length;
|
|
1235
|
+
const orphaned = Object.keys(embIndex).length - activeEmbedded;
|
|
1236
|
+
console.log(`Embedding status: ${activeEmbedded}/${entries.length} memories embedded`);
|
|
1237
|
+
if (orphaned > 0) {
|
|
1238
|
+
console.log(` ${orphaned} orphaned embeddings (run \`hippo embed\` to prune)`);
|
|
1239
|
+
}
|
|
1159
1240
|
const missing = entries.filter((e) => !embIndex[e.id]);
|
|
1160
1241
|
if (missing.length > 0) {
|
|
1161
1242
|
console.log(` ${missing.length} memories need embedding (run \`hippo embed\` to embed them)`);
|
|
@@ -1164,9 +1245,9 @@ async function cmdEmbed(hippoRoot, flags) {
|
|
|
1164
1245
|
}
|
|
1165
1246
|
console.log('Embedding all memories (this may take a moment on first run to download model)...');
|
|
1166
1247
|
const count = await embedAll(hippoRoot);
|
|
1167
|
-
const
|
|
1168
|
-
const
|
|
1169
|
-
console.log(`Done. ${count} new embeddings created. ${Object.keys(
|
|
1248
|
+
const entriesAfter = loadAllEntries(hippoRoot);
|
|
1249
|
+
const embIndexAfter = loadEmbeddingIndex(hippoRoot);
|
|
1250
|
+
console.log(`Done. ${count} new embeddings created. ${Object.keys(embIndexAfter).length}/${entriesAfter.length} total.`);
|
|
1170
1251
|
}
|
|
1171
1252
|
// ---------------------------------------------------------------------------
|
|
1172
1253
|
// Watch command
|