hippo-memory 0.29.3 → 0.30.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 +13 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +129 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -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 +59 -1
- package/dist/consolidate.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +16 -1
- package/dist/db.js.map +1 -1
- package/dist/memory.d.ts +7 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +7 -0
- package/dist/memory.js.map +1 -1
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +54 -6
- package/dist/store.js.map +1 -1
- package/dist/trace.d.ts +34 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +64 -0
- package/dist/trace.js.map +1 -0
- 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 +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,19 @@ hippo recall "data pipeline issues" --budget 2000
|
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
63
|
+
### What's new in v0.30.1
|
|
64
|
+
|
|
65
|
+
- **`hippo recall --layer <L>` is now a strict filter.** Previously the flag was accepted but silently dropped; other layers leaked into results. The RSI demo's `recall --layer trace` now does what it says.
|
|
66
|
+
- **`hippo status` prints a `Trace:` counter.** The new layer is visible in status output.
|
|
67
|
+
- **`hippo --version` / `-v`** works as expected. Previously errored.
|
|
68
|
+
|
|
69
|
+
### What's new in v0.30.0
|
|
70
|
+
|
|
71
|
+
- **Sequence binding for recursive-self-improvement agents.** New `Layer.Trace` memories store ordered `A → B → C → outcome` traces. Agents can `hippo trace record` explicitly, or just call `hippo session complete --outcome success` and let `hippo sleep` auto-promote completed sessions into queryable traces.
|
|
72
|
+
- **`hippo recall --outcome success`** — retrieve only successful prior strategies. The missing RSI primitive: "what worked last time I tried this?"
|
|
73
|
+
- **`examples/rsi-demo/`** — a minimal self-improving agent, deterministic and CI-runnable. Uses traces to learn. Current seed: 20% success on tasks 1-10 rising to 100% on tasks 41-50. Non-zero exit if the learning curve collapses — the demo is also the integration test.
|
|
74
|
+
- **Schema v3 migration, with a regression test that preserves existing data.** Idempotent auto-promotion via indexed `source_session_id`. Four inheritance smoke tests lock the claim that traces get decay / search / replay / physics "for free."
|
|
75
|
+
|
|
63
76
|
### What's new in v0.29.3
|
|
64
77
|
|
|
65
78
|
- **Post-install banner for Claude Code users.** After `npm install -g hippo-memory`, if Claude Code is detected but the Hippo hook isn't wired yet, a three-line message points the user at `hippo init`. Silent on reinstalls or machines without Claude Code. Opt out via `HIPPO_SKIP_POSTINSTALL=1`.
|
package/dist/cli.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* hippo outcome --good | --bad [--id <id>]
|
|
12
12
|
* hippo conflicts [--status <status>] [--json]
|
|
13
13
|
* hippo snapshot <save|show|clear>
|
|
14
|
-
* hippo session <log|show|latest|resume>
|
|
14
|
+
* hippo session <log|show|latest|resume|complete>
|
|
15
15
|
* hippo handoff <create|latest|show>
|
|
16
16
|
* hippo current <show>
|
|
17
17
|
* hippo forget <id>
|
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* hippo outcome --good | --bad [--id <id>]
|
|
12
12
|
* hippo conflicts [--status <status>] [--json]
|
|
13
13
|
* hippo snapshot <save|show|clear>
|
|
14
|
-
* hippo session <log|show|latest|resume>
|
|
14
|
+
* hippo session <log|show|latest|resume|complete>
|
|
15
15
|
* hippo handoff <create|latest|show>
|
|
16
16
|
* hippo current <show>
|
|
17
17
|
* hippo forget <id>
|
|
@@ -28,11 +28,13 @@
|
|
|
28
28
|
import * as path from 'path';
|
|
29
29
|
import * as fs from 'fs';
|
|
30
30
|
import * as os from 'os';
|
|
31
|
+
import { fileURLToPath } from 'node:url';
|
|
31
32
|
import { execFileSync, execSync, spawn } from 'child_process';
|
|
32
33
|
import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths, detectInstalledTools, defaultSleepLogPath, ensureCodexWrapperInstalled, installCodexWrapper, uninstallCodexWrapper, resolveCodexSessionTranscript, resolveCodexWrapperPaths, } from './hooks.js';
|
|
33
34
|
import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, applyOutcome, computeSchemaFit, Layer, DECISION_HALF_LIFE_DAYS, } from './memory.js';
|
|
34
35
|
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';
|
|
35
36
|
import { markRetrieved, estimateTokens, hybridSearch, physicsSearch, explainMatch, textOverlap } from './search.js';
|
|
37
|
+
import { renderTraceContent, parseSteps } from './trace.js';
|
|
36
38
|
import { consolidate } from './consolidate.js';
|
|
37
39
|
import { isEmbeddingAvailable, embedAll, embedMemory, loadEmbeddingIndex, resolveEmbeddingModel, } from './embeddings.js';
|
|
38
40
|
import { loadPhysicsState, resetAllPhysicsState } from './physics-state.js';
|
|
@@ -477,6 +479,32 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
477
479
|
budget, hippoRoot, mmr: mmrEnabled, mmrLambda, minResults,
|
|
478
480
|
});
|
|
479
481
|
}
|
|
482
|
+
// --outcome filter: drop trace entries whose trace_outcome !== target.
|
|
483
|
+
// Non-trace entries pass through unaffected (traces are the only layer with
|
|
484
|
+
// a meaningful outcome; filtering non-traces by outcome would be incoherent).
|
|
485
|
+
const outcomeFilter = flags['outcome'] !== undefined ? String(flags['outcome']).trim() : '';
|
|
486
|
+
if (outcomeFilter) {
|
|
487
|
+
const validOutcomes = ['success', 'failure', 'partial'];
|
|
488
|
+
if (!validOutcomes.includes(outcomeFilter)) {
|
|
489
|
+
console.error(`Invalid --outcome: "${outcomeFilter}". Must be one of: ${validOutcomes.join(', ')}.`);
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
results = results.filter((r) => {
|
|
493
|
+
if (r.entry.layer !== Layer.Trace)
|
|
494
|
+
return true;
|
|
495
|
+
return r.entry.trace_outcome === outcomeFilter;
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
// --layer filter: strict, drops entries whose layer does not match.
|
|
499
|
+
const layerFilter = flags['layer'] !== undefined ? String(flags['layer']).trim() : '';
|
|
500
|
+
if (layerFilter) {
|
|
501
|
+
const validLayers = Object.values(Layer);
|
|
502
|
+
if (!validLayers.includes(layerFilter)) {
|
|
503
|
+
console.error(`Invalid --layer: "${layerFilter}". Must be one of: ${validLayers.join(', ')}.`);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
}
|
|
506
|
+
results = results.filter((r) => r.entry.layer === layerFilter);
|
|
507
|
+
}
|
|
480
508
|
if (limit < results.length) {
|
|
481
509
|
results = results.slice(0, limit);
|
|
482
510
|
}
|
|
@@ -510,10 +538,13 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
510
538
|
tokens: r.tokens,
|
|
511
539
|
tags: r.entry.tags,
|
|
512
540
|
content: r.entry.content,
|
|
541
|
+
layer: r.entry.layer,
|
|
513
542
|
};
|
|
543
|
+
if (r.entry.layer === Layer.Trace) {
|
|
544
|
+
base.trace_outcome = r.entry.trace_outcome;
|
|
545
|
+
}
|
|
514
546
|
if (showWhy) {
|
|
515
547
|
const explanation = explainMatch(query, r);
|
|
516
|
-
base.layer = r.entry.layer;
|
|
517
548
|
base.confidence = resolveConfidence(r.entry);
|
|
518
549
|
base.source = isGlobal ? 'global' : 'local';
|
|
519
550
|
base.reason = explanation.reason;
|
|
@@ -826,6 +857,50 @@ async function cmdEval(hippoRoot, corpusPath, flags) {
|
|
|
826
857
|
}
|
|
827
858
|
}
|
|
828
859
|
}
|
|
860
|
+
function cmdTraceRecord(hippoRoot, flags) {
|
|
861
|
+
requireInit(hippoRoot);
|
|
862
|
+
const task = String(flags['task'] ?? '').trim();
|
|
863
|
+
const stepsJson = String(flags['steps'] ?? '').trim();
|
|
864
|
+
const outcome = String(flags['outcome'] ?? '').trim();
|
|
865
|
+
const validOutcomes = ['success', 'failure', 'partial'];
|
|
866
|
+
if (!task || !stepsJson || !outcome) {
|
|
867
|
+
console.error('Usage: hippo trace record --task <t> --steps <json> --outcome <success|failure|partial> [--session <id>] [--tag <t>]');
|
|
868
|
+
process.exit(1);
|
|
869
|
+
}
|
|
870
|
+
if (!validOutcomes.includes(outcome)) {
|
|
871
|
+
console.error(`Invalid outcome: "${outcome}". Must be one of: ${validOutcomes.join(', ')}.`);
|
|
872
|
+
process.exit(1);
|
|
873
|
+
}
|
|
874
|
+
let steps;
|
|
875
|
+
try {
|
|
876
|
+
steps = parseSteps(stepsJson);
|
|
877
|
+
}
|
|
878
|
+
catch (err) {
|
|
879
|
+
console.error(String(err instanceof Error ? err.message : err));
|
|
880
|
+
process.exit(1);
|
|
881
|
+
}
|
|
882
|
+
const sessionId = String(flags['session'] ?? '').trim() || null;
|
|
883
|
+
const rawTags = flags['tag'];
|
|
884
|
+
const tags = Array.isArray(rawTags)
|
|
885
|
+
? rawTags.map((t) => String(t))
|
|
886
|
+
: rawTags !== undefined
|
|
887
|
+
? [String(rawTags)]
|
|
888
|
+
: [];
|
|
889
|
+
const content = renderTraceContent({
|
|
890
|
+
task,
|
|
891
|
+
steps,
|
|
892
|
+
outcome: outcome,
|
|
893
|
+
});
|
|
894
|
+
const entry = createMemory(content, {
|
|
895
|
+
layer: Layer.Trace,
|
|
896
|
+
tags,
|
|
897
|
+
source: String(flags['source'] ?? 'cli'),
|
|
898
|
+
trace_outcome: outcome,
|
|
899
|
+
source_session_id: sessionId,
|
|
900
|
+
});
|
|
901
|
+
writeEntry(hippoRoot, entry);
|
|
902
|
+
console.log(`Recorded trace ${entry.id} (outcome=${outcome}, ${steps.length} steps)`);
|
|
903
|
+
}
|
|
829
904
|
function cmdTrace(hippoRoot, id, flags) {
|
|
830
905
|
requireInit(hippoRoot);
|
|
831
906
|
const asJson = Boolean(flags['json']);
|
|
@@ -1493,6 +1568,7 @@ function cmdStatus(hippoRoot) {
|
|
|
1493
1568
|
[Layer.Buffer]: 0,
|
|
1494
1569
|
[Layer.Episodic]: 0,
|
|
1495
1570
|
[Layer.Semantic]: 0,
|
|
1571
|
+
[Layer.Trace]: 0,
|
|
1496
1572
|
};
|
|
1497
1573
|
const byConfidence = {
|
|
1498
1574
|
verified: 0,
|
|
@@ -1521,6 +1597,7 @@ function cmdStatus(hippoRoot) {
|
|
|
1521
1597
|
console.log(` Buffer: ${byLayer[Layer.Buffer]}`);
|
|
1522
1598
|
console.log(` Episodic: ${byLayer[Layer.Episodic]}`);
|
|
1523
1599
|
console.log(` Semantic: ${byLayer[Layer.Semantic]}`);
|
|
1600
|
+
console.log(` Trace: ${byLayer[Layer.Trace]}`);
|
|
1524
1601
|
const conflictCount = listMemoryConflicts(hippoRoot).length;
|
|
1525
1602
|
console.log(`Pinned: ${pinned}`);
|
|
1526
1603
|
console.log(`At risk (<0.2): ${atRisk}`);
|
|
@@ -1880,6 +1957,32 @@ function cmdSession(hippoRoot, args, flags) {
|
|
|
1880
1957
|
printSessionEvents(events);
|
|
1881
1958
|
return;
|
|
1882
1959
|
}
|
|
1960
|
+
if (subcommand === 'complete') {
|
|
1961
|
+
const outcome = String(flags['outcome'] ?? '').trim();
|
|
1962
|
+
const summary = String(flags['summary'] ?? '').trim();
|
|
1963
|
+
const validOutcomes = ['success', 'failure', 'partial'];
|
|
1964
|
+
if (!sessionId) {
|
|
1965
|
+
console.error('Usage: hippo session complete --session <session-id> --outcome <success|failure|partial> [--summary "..."]');
|
|
1966
|
+
process.exit(1);
|
|
1967
|
+
}
|
|
1968
|
+
if (!validOutcomes.includes(outcome)) {
|
|
1969
|
+
console.error(`Invalid outcome: "${outcome}". Must be one of: ${validOutcomes.join(', ')}.`);
|
|
1970
|
+
process.exit(1);
|
|
1971
|
+
}
|
|
1972
|
+
const metadata = { ended_at: new Date().toISOString() };
|
|
1973
|
+
if (summary)
|
|
1974
|
+
metadata.summary = summary;
|
|
1975
|
+
const event = appendSessionEvent(hippoRoot, {
|
|
1976
|
+
session_id: sessionId,
|
|
1977
|
+
task: task || null,
|
|
1978
|
+
event_type: 'session_complete',
|
|
1979
|
+
content: outcome,
|
|
1980
|
+
source: String(flags['source'] ?? 'cli'),
|
|
1981
|
+
metadata,
|
|
1982
|
+
});
|
|
1983
|
+
console.log(`Completed session ${event.session_id} with outcome=${outcome} (event #${event.id})`);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1883
1986
|
if (subcommand === 'resume') {
|
|
1884
1987
|
const handoff = loadLatestHandoff(hippoRoot, sessionId || undefined);
|
|
1885
1988
|
if (!handoff) {
|
|
@@ -1910,7 +2013,7 @@ function cmdSession(hippoRoot, args, flags) {
|
|
|
1910
2013
|
console.log(lines.join('\n'));
|
|
1911
2014
|
return;
|
|
1912
2015
|
}
|
|
1913
|
-
console.error('Usage: hippo session <log|show|latest|resume>');
|
|
2016
|
+
console.error('Usage: hippo session <log|show|latest|resume|complete>');
|
|
1914
2017
|
process.exit(1);
|
|
1915
2018
|
}
|
|
1916
2019
|
function printHandoff(handoff) {
|
|
@@ -3377,6 +3480,21 @@ Examples:
|
|
|
3377
3480
|
const { command, args, flags } = parseArgs(process.argv);
|
|
3378
3481
|
const hippoRoot = getHippoRoot(process.cwd());
|
|
3379
3482
|
async function main() {
|
|
3483
|
+
if (command === '--version' || command === '-v' || flags['version']) {
|
|
3484
|
+
const __filename_local = fileURLToPath(import.meta.url);
|
|
3485
|
+
const __dirname_local = path.dirname(__filename_local);
|
|
3486
|
+
const pkgPath = path.join(__dirname_local, '..', 'package.json');
|
|
3487
|
+
let pkgJson;
|
|
3488
|
+
try {
|
|
3489
|
+
pkgJson = fs.readFileSync(pkgPath, 'utf-8');
|
|
3490
|
+
}
|
|
3491
|
+
catch {
|
|
3492
|
+
pkgJson = fs.readFileSync(path.join(__dirname_local, '..', '..', 'package.json'), 'utf-8');
|
|
3493
|
+
}
|
|
3494
|
+
const { version } = JSON.parse(pkgJson);
|
|
3495
|
+
console.log(version);
|
|
3496
|
+
process.exit(0);
|
|
3497
|
+
}
|
|
3380
3498
|
maybeAutoInstallCodexWrapper(command, args);
|
|
3381
3499
|
switch (command) {
|
|
3382
3500
|
case 'init':
|
|
@@ -3415,12 +3533,16 @@ async function main() {
|
|
|
3415
3533
|
break;
|
|
3416
3534
|
}
|
|
3417
3535
|
case 'trace': {
|
|
3418
|
-
const
|
|
3419
|
-
if (
|
|
3420
|
-
|
|
3536
|
+
const sub = args[0] ? String(args[0]) : '';
|
|
3537
|
+
if (sub === 'record') {
|
|
3538
|
+
cmdTraceRecord(hippoRoot, flags);
|
|
3539
|
+
break;
|
|
3540
|
+
}
|
|
3541
|
+
if (!sub) {
|
|
3542
|
+
console.error('Usage: hippo trace <memory-id> | hippo trace record --task <t> --steps <json> --outcome <o>');
|
|
3421
3543
|
process.exit(1);
|
|
3422
3544
|
}
|
|
3423
|
-
cmdTrace(hippoRoot,
|
|
3545
|
+
cmdTrace(hippoRoot, sub, flags);
|
|
3424
3546
|
break;
|
|
3425
3547
|
}
|
|
3426
3548
|
case 'refine':
|