hippo-memory 1.8.1 → 1.10.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.
- package/README.md +32 -0
- package/bin/hippo.js +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +118 -19
- package/dist/cli.js.map +1 -1
- package/dist/embeddings.d.ts +34 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +74 -14
- package/dist/embeddings.js.map +1 -1
- package/dist/rerankers/cross-encoder.d.ts +18 -0
- package/dist/rerankers/cross-encoder.d.ts.map +1 -0
- package/dist/rerankers/cross-encoder.js +90 -0
- package/dist/rerankers/cross-encoder.js.map +1 -0
- package/dist/rerankers/index.d.ts +4 -0
- package/dist/rerankers/index.d.ts.map +1 -0
- package/dist/rerankers/index.js +16 -0
- package/dist/rerankers/index.js.map +1 -0
- package/dist/rerankers/llm.d.ts +15 -0
- package/dist/rerankers/llm.d.ts.map +1 -0
- package/dist/rerankers/llm.js +77 -0
- package/dist/rerankers/llm.js.map +1 -0
- package/dist/rerankers/types.d.ts +30 -0
- package/dist/rerankers/types.d.ts.map +1 -0
- package/dist/rerankers/types.js +2 -0
- package/dist/rerankers/types.js.map +1 -0
- package/dist/rrf.d.ts +43 -0
- package/dist/rrf.d.ts.map +1 -0
- package/dist/rrf.js +61 -0
- package/dist/rrf.js.map +1 -0
- package/dist/search.d.ts +17 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +24 -19
- package/dist/search.js.map +1 -1
- package/dist/server-detect.d.ts +25 -4
- package/dist/server-detect.d.ts.map +1 -1
- package/dist/server-detect.js +125 -5
- package/dist/server-detect.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19 -2
- package/dist/server.js.map +1 -1
- package/dist/src/cli.js +118 -19
- package/dist/src/cli.js.map +1 -1
- package/dist/src/embeddings.js +74 -14
- package/dist/src/embeddings.js.map +1 -1
- package/dist/src/rerankers/cross-encoder.js +90 -0
- package/dist/src/rerankers/cross-encoder.js.map +1 -0
- package/dist/src/rerankers/index.js +16 -0
- package/dist/src/rerankers/index.js.map +1 -0
- package/dist/src/rerankers/llm.js +77 -0
- package/dist/src/rerankers/llm.js.map +1 -0
- package/dist/src/rerankers/types.js +2 -0
- package/dist/src/rerankers/types.js.map +1 -0
- package/dist/src/rrf.js +61 -0
- package/dist/src/rrf.js.map +1 -0
- package/dist/src/search.js +24 -19
- package/dist/src/search.js.map +1 -1
- package/dist/src/server-detect.js +125 -5
- package/dist/src/server-detect.js.map +1 -1
- package/dist/src/server.js +19 -2
- package/dist/src/server.js.map +1 -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 +2 -1
package/README.md
CHANGED
|
@@ -85,6 +85,38 @@ hippo recall "data pipeline issues" --budget 2000
|
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
88
|
+
### What's new in v1.10.0
|
|
89
|
+
|
|
90
|
+
- **Server and lifecycle hardening.** Closes the `TODOS.md` "server / lifecycle hardening" cluster (deferred follow-ups from the v0.37 server-mode work, the v0.40 security pass, and the A3 envelope review), six items in all. `detectServer` is now async and confirms a recorded server is genuinely this hippo process by matching a `/health` `started_at` before the CLI routes to it (H1). The pidfile carries a `schema` version (L3). `hippo serve` refuses to start when a live peer already serves the hippoRoot (H3). The 413 over-cap-body path closes the socket instead of draining the rest (M3). A `HIPPO_REQUIRE_SERVER` env knob turns a missing server into a loud error instead of a silent direct-mode fallback that discards `HIPPO_API_KEY` (H2). And `hippo forget --archive --reason` gives raw, append-only memories a real removal path via `archiveRaw` instead of a misleading "not found" (A3).
|
|
91
|
+
- **Reviewed via the dev-framework chain.** self-review, an independent code review, a cross-model codex (gpt-5.5) pass, and a security pass. Four findings were fixed before ship: the `forget --archive` server-routing bypass, an unbounded `/health` response parse, a timeout that wrongly unlinked a busy server's pidfile, and pidfile-url validation against off-box redirection. Full suite green: 216 files, 1557 tests.
|
|
92
|
+
|
|
93
|
+
### What's new in v1.9.3
|
|
94
|
+
|
|
95
|
+
- **Reranker review-tail patch.** Closes the three follow-ups raised on PR #25: `src/rerankers/llm.ts` now wires `AbortController` + `setTimeout` around the fetch (default 30 s, overridable via `HIPPO_LLM_RERANKER_TIMEOUT_MS`) so recall never hangs on a wedged endpoint; `src/rerankers/cross-encoder.ts` emits a single `console.warn` on first identity-fallback per process so silent fallback no longer masquerades as a working reranker; the orphan `RerankSignals` type (sole consumer retracted in v1.9.1) is removed at both the re-export and the definition.
|
|
96
|
+
- **Version alignment.** `package.json` bumped 1.8.1 → 1.9.3. v1.9.0 / v1.9.1 / v1.9.2 were on-master research milestones never published to npm; v1.9.3 is the first published `1.9.x` release and carries the cumulative scope from F6 (rerankers) through F13 (chunk-per-turn) plus the F10 HARD RETRACTION.
|
|
97
|
+
- **Mechanism cumulative-null status unaffected.** Per `docs/RETRACTION.md:94-113`. No `src/` change in this patch touches the dlPFC goal-stack mechanism. **This release does not re-assert the retracted −10pp magnitude.**
|
|
98
|
+
|
|
99
|
+
### What's new in v1.9.2
|
|
100
|
+
|
|
101
|
+
- **F13 chunk-per-turn LongMemEval R@5 = 86.8 on oracle (Gate-B PASS).** Plan F13 (`docs/evals/2026-05-12-r5-track6-chunk-per-turn-prereg.md`) addresses the structural pathology that limited every prior LongMemEval track (F8–F12): sessions in `data/longmemeval_oracle.json` are ~14k chars median (~3,500 tokens), but the embedders we can reach (MiniLM, BGE-base, multilingual-e5-large) cap at 512–514 tokens. Every prior track embedded only the first ~2 turns of each 12-turn session and truncated the rest. F13 replaces session-level embedding with turn-level embedding (10,866 turns over the 940 oracle sessions, max-pool by `session_id` at retrieval). Gate-A PASS (10,866 turns, all 940 sessions covered, 768-dim normalized). **Gate-B PASS:** F13 + F9 sub-agent rerank stack R@5 = 86.8 on `data/longmemeval_oracle.json` (threshold ≥ 83.2 = F11+F9 deployable best 78.2 + 5pp; margin 3.6). R@1 = 70.8, R@10 = 90.2, R@20 = 93.4.
|
|
102
|
+
- **Roadmap target met (oracle split).** R@5 ≥ 85% was NON-binding per every prior prereg; observed 86.8 on `data/longmemeval_oracle.json` as of this release. Descriptive characterisation; not a re-assertion of any retracted magnitude.
|
|
103
|
+
- **Split-mismatch with gbrain (unchanged).** `longmemeval_oracle` carries 3 sessions per haystack; gbrain v0.28.8's 97.60 figure is on `longmemeval_s_cleaned` (~40 sessions per haystack) with OpenAI `text-embedding-3-large@1536`. Both HF Hub and OpenAI API are host-blocked from this sandbox (verified 2026-05-12). F13's 86.8 is NOT directly comparable to gbrain's 97.60.
|
|
104
|
+
- **F12 retracted.** Plan F12 (`docs/evals/2026-05-11-r5-track5-e5-large-top100-prereg.md`) vendored `intfloat/multilingual-e5-large` and widened the candidate pool to top-100. Gate-A PASS; Gate-B FAIL with best variant R@5 = 78.8 (threshold 83.2). HARD RETRACTION executed: `hippo_store2/` reverted to BGE-base; the `prefixFor` / `preferredBackend` dispatch helpers stay in `src/embeddings.ts` per the dispatch-shape carve-out (they return the legacy behaviour for non-e5 models).
|
|
105
|
+
- **No `src/` changes in v1.9.2.** F13 is implemented as `benchmarks/longmemeval/chunk_per_turn_{embed,retrieve}.mjs` and reuses F11/F12's existing dispatch helpers. The cumulative-null status of the dlPFC goal-stack mechanism (`docs/RETRACTION.md:94-113`) is unaffected. **This release does not re-assert the retracted −10pp magnitude.**
|
|
106
|
+
|
|
107
|
+
### What's new in v1.9.1
|
|
108
|
+
|
|
109
|
+
- **F10 features-reranker retraction.** Plan F10 (`docs/plans/2026-05-11-r5-track3-richer-ingest.md`) tested whether populating entry-level signals via 19 Claude-sub-agent invocations would let the features reranker move R@5 above features-default + 5pp on LongMemEval. Observed: features-enriched R@5 = 59.2 vs features-default R@5 = 75.8 (same bge-base embedding model), a 21.6pp shortfall against the binding gate. Per the prereg's HARD RETRACTION clause, `src/rerankers/features.ts` + its test + its micro-fixture + its dispatcher case are removed in v1.9.1. The Track 2 cross-encoder and Track 3 LLM-rerank skeletons are preserved. **This release does not re-assert the retracted −10pp magnitude.** Per `docs/RETRACTION.md`.
|
|
110
|
+
- **F11 embedding upgrade tested and documented (not shipped as default).** Plan F11 (`docs/plans/2026-05-11-r5-track4-embedding-upgrade.md`) swapped `Xenova/all-MiniLM-L6-v2` for `BAAI/bge-base-en-v1.5` (768-dim, CLS pooling). Gate-A PASS; Gate-B FAIL (R@5 = 77.0% vs threshold 81.8%). The `poolingFor` per-model dispatch in `src/embeddings.ts` and the `--model` flag in `scripts/fetch_embedding_model.mjs` ship; MiniLM remains the project default.
|
|
111
|
+
- **Cross-track R@5 status (as of v1.9.1):** F8 hybrid tuning (MiniLM) 76.8, F9 v2 sub-agent LLM rerank (MiniLM) 78.0, F11 bge-base baseline 77.0, F11+F9 stack (BGE-base + sub-agent rerank) 78.2 — cross-track best at v1.9.1 — F10 features-enriched (retracted) 59.2. Roadmap target R@5 ≥ 85% was NOT MET at v1.9.1. NON-binding per each prereg. *(Superseded in v1.9.2 by F13 + F9 stack R@5 = 86.8 on oracle.)*
|
|
112
|
+
|
|
113
|
+
### What's new in v1.9.0
|
|
114
|
+
|
|
115
|
+
- **F6 reranker hardening shipped.** New `RerankerFn` seam in `hybridSearch` with three reranker tracks: Track 1 features (`MemoryEntry`-level signals, no external deps), Track 2 cross-encoder (MS-MARCO MiniLM via optional `@xenova/transformers`, identity-fallback on load failure), Track 3 LLM (env-gated skeleton against an OpenAI-compatible endpoint). Opt in via `hippo recall --reranker <name>`.
|
|
116
|
+
- **Workload-validity verdicts on the LongMemEval sweep** (`docs/evals/2026-05-10-f6-reranker-result.md`, prereg `docs/evals/2026-05-10-f6-reranker-prereg.md`): Gate-A (firing rate, binding) PASS for the features track, PASS-with-caveat for cross-encoder (500/500 invocations all took the identity-fallback branch — HF model download was blocked in the test environment, so this is NOT a real cross-encoder evaluation). Gate-B (hyperparameter discrimination, binding) FAIL — features_topk{20,50,100} produced byte-identical R@K, so no per-hyperparameter R@5 effect is claimed.
|
|
117
|
+
- **Roadmap R@5 ≥ 85% target NOT met on the workload tested.** Observed R@5 = 75.4% (features, all three top-K settings) and 75.6% (baseline). Per the prereg this is descriptive characterisation, not a binding gate; the mechanism ships, and a real attempt at the target requires either a real cross-encoder evaluation (HF access) or a richer ingest path that populates entry-level reranker signals.
|
|
118
|
+
- **This release does not re-assert the retracted −10pp magnitude.** Per `docs/RETRACTION.md`. The dlPFC goal-stack cumulative-null status (`docs/RETRACTION.md:94-113`) is independent of this release.
|
|
119
|
+
|
|
88
120
|
### What's new in v1.8.1
|
|
89
121
|
|
|
90
122
|
- **v1.8 prereg's v1.9 LongMemEval cross-validation pre-commitment RETRACTED.** Outside-voice review on two iterations of the v1.9 plan found six structural barriers (canonical harness bypasses the boost path; ingest tag namespace excludes content-derived stems; pushGoal API field mismatch; depth-cap suspension; trigger AND clause unreachable; workload-validity gate ceremonial). Per Root Cause Over Patches, public retraction over re-architecture. **This release does not re-assert the retracted −10pp magnitude.** Per `docs/RETRACTION.md`.
|
package/bin/hippo.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import '../dist/cli.js';
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import '../dist/cli.js';
|
package/dist/cli.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* hippo session <log|show|latest|resume|complete>
|
|
15
15
|
* hippo handoff <create|latest|show>
|
|
16
16
|
* hippo current <show>
|
|
17
|
-
* hippo forget <id>
|
|
17
|
+
* hippo forget <id> [--archive --reason "<why>"]
|
|
18
18
|
* hippo inspect <id>
|
|
19
19
|
* hippo embed [--status]
|
|
20
20
|
* hippo watch "<command>"
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* hippo session <log|show|latest|resume|complete>
|
|
15
15
|
* hippo handoff <create|latest|show>
|
|
16
16
|
* hippo current <show>
|
|
17
|
-
* hippo forget <id>
|
|
17
|
+
* hippo forget <id> [--archive --reason "<why>"]
|
|
18
18
|
* hippo inspect <id>
|
|
19
19
|
* hippo embed [--status]
|
|
20
20
|
* hippo watch "<command>"
|
|
@@ -64,6 +64,7 @@ import { runFeatureEval, formatResult, resultToBaseline, detectRegressions } fro
|
|
|
64
64
|
import { refineStore } from './refine-llm.js';
|
|
65
65
|
import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
|
|
66
66
|
import { multihopSearch } from './multihop.js';
|
|
67
|
+
import { getReranker } from './rerankers/index.js';
|
|
67
68
|
import { computeSalience } from './salience.js';
|
|
68
69
|
import { computeAmbientState, renderAmbientSummary } from './ambient.js';
|
|
69
70
|
import { listDlq, replayDlqEntry } from './connectors/slack/dlq.js';
|
|
@@ -116,6 +117,19 @@ function requireInit(hippoRoot) {
|
|
|
116
117
|
process.exit(1);
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* H2: when HIPPO_REQUIRE_SERVER is set, the CLI must not silently fall back to
|
|
122
|
+
* direct DB mode — a missing server then masks a real misconfiguration (the
|
|
123
|
+
* configured HIPPO_API_KEY is also silently discarded on fallback). Throws a
|
|
124
|
+
* clear, actionable error in that case; a no-op when the knob is unset, so
|
|
125
|
+
* default behaviour is unchanged.
|
|
126
|
+
*/
|
|
127
|
+
function failIfServerRequired(reason) {
|
|
128
|
+
if (process.env['HIPPO_REQUIRE_SERVER']) {
|
|
129
|
+
throw new Error(`hippo: HIPPO_REQUIRE_SERVER is set but ${reason}. ` +
|
|
130
|
+
`Start \`hippo serve\`, or unset HIPPO_REQUIRE_SERVER to allow direct-mode fallback.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
119
133
|
/**
|
|
120
134
|
* Run an HTTP-routed command if a `hippo serve` instance is detected for
|
|
121
135
|
* `hippoRoot`. Returns:
|
|
@@ -126,11 +140,16 @@ function requireInit(hippoRoot) {
|
|
|
126
140
|
* and the caller should fall back to the direct path.
|
|
127
141
|
*
|
|
128
142
|
* Per the A1 plan footgun #1: stale pidfiles must self-heal, not crash.
|
|
143
|
+
* H2: when HIPPO_REQUIRE_SERVER is set, both fallback paths throw instead of
|
|
144
|
+
* returning false, so a missing server fails loudly rather than silently
|
|
145
|
+
* degrading to direct mode.
|
|
129
146
|
*/
|
|
130
147
|
async function runViaServerIfAvailable(hippoRoot, httpFn) {
|
|
131
|
-
const info = detectServer(hippoRoot);
|
|
132
|
-
if (!info)
|
|
148
|
+
const info = await detectServer(hippoRoot);
|
|
149
|
+
if (!info) {
|
|
150
|
+
failIfServerRequired('no running server was detected for this hippoRoot');
|
|
133
151
|
return false;
|
|
152
|
+
}
|
|
134
153
|
const apiKey = process.env['HIPPO_API_KEY'];
|
|
135
154
|
try {
|
|
136
155
|
await httpFn(info, apiKey);
|
|
@@ -138,6 +157,7 @@ async function runViaServerIfAvailable(hippoRoot, httpFn) {
|
|
|
138
157
|
}
|
|
139
158
|
catch (err) {
|
|
140
159
|
if (client.isConnectionRefused(err)) {
|
|
160
|
+
failIfServerRequired('the server pidfile was stale (connection refused)');
|
|
141
161
|
console.error('hippo: stale server pidfile detected, falling back to direct mode');
|
|
142
162
|
removePidfile(hippoRoot);
|
|
143
163
|
return false;
|
|
@@ -701,17 +721,21 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
701
721
|
physicsConfig: config.physics,
|
|
702
722
|
minResults,
|
|
703
723
|
scope: recallActiveScope,
|
|
724
|
+
includeSuperseded,
|
|
725
|
+
asOf,
|
|
704
726
|
});
|
|
705
727
|
}
|
|
706
728
|
else if (hasGlobal) {
|
|
707
729
|
// Use searchBothHybrid for merged results with embedding support
|
|
708
730
|
results = await searchBothHybrid(query, hippoRoot, globalRoot, {
|
|
709
731
|
budget, mmr: mmrEnabled, mmrLambda, localBump, minResults, scope: recallActiveScope, tenantId,
|
|
732
|
+
includeSuperseded, asOf,
|
|
710
733
|
});
|
|
711
734
|
}
|
|
712
735
|
else {
|
|
713
736
|
results = await hybridSearch(query, localEntries, {
|
|
714
737
|
budget, hippoRoot, mmr: mmrEnabled, mmrLambda, minResults, scope: recallActiveScope,
|
|
738
|
+
includeSuperseded, asOf,
|
|
715
739
|
});
|
|
716
740
|
}
|
|
717
741
|
// ACC EVC-adaptive recall (RESEARCH.md §PFC.ACC). When the initial top-K is
|
|
@@ -818,6 +842,39 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
818
842
|
})
|
|
819
843
|
.sort((a, b) => b.score - a.score);
|
|
820
844
|
}
|
|
845
|
+
// F6 reranker pass (docs/plans/2026-05-10-f6-reranker-hardening.md). When
|
|
846
|
+
// --reranker <name> is set, look up the reranker fn from the registry
|
|
847
|
+
// (src/rerankers/index.ts) and apply it to the top-K candidates. The
|
|
848
|
+
// reranker reorders (and may rescale) results; the post-budget set is
|
|
849
|
+
// returned. Default off; opt-in via --reranker <cross-encoder|llm>. The
|
|
850
|
+
// structurally similar --rerank-utility block above is the OFC MVP and is
|
|
851
|
+
// independent — both can run in the same recall, with --rerank-utility
|
|
852
|
+
// applied first. Available rerankers: cross-encoder, llm (see
|
|
853
|
+
// src/rerankers/index.ts). The Track 1 `features` reranker was removed in
|
|
854
|
+
// v1.9.1 per the F10 HARD RETRACTION; it is no longer a valid value.
|
|
855
|
+
const rerankerName = flags['reranker'] !== undefined ? String(flags['reranker']).trim() : '';
|
|
856
|
+
if (rerankerName) {
|
|
857
|
+
const rerankerFn = getReranker(rerankerName);
|
|
858
|
+
if (rerankerFn) {
|
|
859
|
+
const topK = flags['reranker-top-k'] !== undefined
|
|
860
|
+
? parseInt(String(flags['reranker-top-k']), 10)
|
|
861
|
+
: 50;
|
|
862
|
+
const head = results.slice(0, topK);
|
|
863
|
+
const tail = results.slice(topK);
|
|
864
|
+
const rerankInput = head.map((r, i) => ({ ...r, preRerankRank: i + 1 }));
|
|
865
|
+
const reranked = await rerankerFn(query, rerankInput, { topK });
|
|
866
|
+
// Copy rerankScore into score so downstream blocks (--goal, goal-stack,
|
|
867
|
+
// salience) that sort by `r.score` honor the reranker's order rather
|
|
868
|
+
// than unwinding it. Original score is preserved on rerankScore's
|
|
869
|
+
// input, but downstream sorters key on `score`.
|
|
870
|
+
const withPostRank = reranked.map((r, i) => ({
|
|
871
|
+
...r,
|
|
872
|
+
score: r.rerankScore,
|
|
873
|
+
postRerankRank: i + 1,
|
|
874
|
+
}));
|
|
875
|
+
results = [...withPostRank, ...tail];
|
|
876
|
+
}
|
|
877
|
+
}
|
|
821
878
|
// dlPFC goal-conditioned recall MVP (RESEARCH.md §PFC.dlPFC). When --goal
|
|
822
879
|
// <tag> is set, memories whose `tags` array contains the goal tag receive
|
|
823
880
|
// a 1.5x score boost and results are re-sorted. The full dlPFC spec
|
|
@@ -2325,20 +2382,49 @@ function cmdOutcome(hippoRoot, flags) {
|
|
|
2325
2382
|
}
|
|
2326
2383
|
console.log(`Applied ${good ? 'positive' : 'negative'} outcome to ${updated} memor${updated === 1 ? 'y' : 'ies'}`);
|
|
2327
2384
|
}
|
|
2328
|
-
function cmdForget(hippoRoot, id) {
|
|
2385
|
+
function cmdForget(hippoRoot, id, flags) {
|
|
2329
2386
|
requireInit(hippoRoot);
|
|
2330
2387
|
const ctx = {
|
|
2331
2388
|
hippoRoot,
|
|
2332
2389
|
tenantId: resolveTenantId({}),
|
|
2333
2390
|
actor: 'cli',
|
|
2334
2391
|
};
|
|
2392
|
+
// A3: raw memories (Slack / GitHub connector ingestion) are append-only — a
|
|
2393
|
+
// BEFORE-DELETE trigger aborts any delete. archiveRaw is the sanctioned
|
|
2394
|
+
// removal path; it records ctx.actor as the archiver for provenance.
|
|
2395
|
+
if (flags['archive'] === true) {
|
|
2396
|
+
const reason = typeof flags['reason'] === 'string' ? flags['reason'] : null;
|
|
2397
|
+
if (!reason) {
|
|
2398
|
+
console.error('hippo forget --archive requires --reason "<why>" (recorded on the archive).');
|
|
2399
|
+
process.exit(1);
|
|
2400
|
+
}
|
|
2401
|
+
try {
|
|
2402
|
+
api.archiveRaw(ctx, id, reason);
|
|
2403
|
+
updateStats(hippoRoot, { forgotten: 1 });
|
|
2404
|
+
console.log(`Archived ${id}`);
|
|
2405
|
+
}
|
|
2406
|
+
catch (err) {
|
|
2407
|
+
console.error(`Could not archive ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2408
|
+
process.exit(1);
|
|
2409
|
+
}
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2335
2412
|
try {
|
|
2336
2413
|
api.forget(ctx, id);
|
|
2337
2414
|
updateStats(hippoRoot, { forgotten: 1 });
|
|
2338
2415
|
console.log(`Forgot ${id}`);
|
|
2339
2416
|
}
|
|
2340
|
-
catch {
|
|
2341
|
-
|
|
2417
|
+
catch (err) {
|
|
2418
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2419
|
+
if (/append-only/i.test(msg)) {
|
|
2420
|
+
// The delete was refused by the append-only trigger — this is a raw
|
|
2421
|
+
// memory, not a missing one. Point the user at the archive path.
|
|
2422
|
+
console.error(`Cannot forget ${id}: it is a raw, append-only memory. ` +
|
|
2423
|
+
`Archive it instead: hippo forget ${id} --archive --reason "<why>"`);
|
|
2424
|
+
}
|
|
2425
|
+
else {
|
|
2426
|
+
console.error(`Memory not found: ${id}`);
|
|
2427
|
+
}
|
|
2342
2428
|
process.exit(1);
|
|
2343
2429
|
}
|
|
2344
2430
|
}
|
|
@@ -4649,6 +4735,12 @@ Commands:
|
|
|
4649
4735
|
= score * (0.5 + 0.5 * strength) * (1 - cost_factor)
|
|
4650
4736
|
where cost_factor = min(0.3, tokens / 10000). Re-sorts
|
|
4651
4737
|
results by utility. Default off. RESEARCH.md §PFC.OFC.
|
|
4738
|
+
--reranker <name> Apply a reranker pass after retrieval
|
|
4739
|
+
(cross-encoder|llm). Looks up the named
|
|
4740
|
+
reranker from src/rerankers/index.ts and re-orders
|
|
4741
|
+
the top-K candidates. Default unset (no reranker).
|
|
4742
|
+
See docs/plans/2026-05-10-f6-reranker-hardening.md.
|
|
4743
|
+
--reranker-top-k <n> Cap candidates passed to the reranker (default 50).
|
|
4652
4744
|
--goal <tag> dlPFC goal-conditioned recall: memories tagged with
|
|
4653
4745
|
the goal tag get a 1.5x score boost and results are
|
|
4654
4746
|
re-sorted. Default off. RESEARCH.md §PFC.dlPFC.
|
|
@@ -4797,6 +4889,8 @@ Commands:
|
|
|
4797
4889
|
current show Active task + recent session events (default)
|
|
4798
4890
|
--json Output as JSON
|
|
4799
4891
|
forget <id> Force remove a memory
|
|
4892
|
+
--archive Archive a raw (append-only) memory instead of deleting
|
|
4893
|
+
--reason "<why>" Reason recorded on the archive (required with --archive)
|
|
4800
4894
|
inspect <id> Show full memory detail
|
|
4801
4895
|
embed Embed all memories for semantic search
|
|
4802
4896
|
--status Show embedding coverage
|
|
@@ -5239,19 +5333,24 @@ async function main() {
|
|
|
5239
5333
|
console.error('Please provide a memory ID.');
|
|
5240
5334
|
process.exit(1);
|
|
5241
5335
|
}
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5336
|
+
// A3: --archive is a direct-DB operation (raw archival via archiveRaw);
|
|
5337
|
+
// the HTTP forget route does not carry it, so archive requests always
|
|
5338
|
+
// take the direct path rather than silently no-op'ing over a server.
|
|
5339
|
+
if (flags['archive'] !== true) {
|
|
5340
|
+
const routed = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
5341
|
+
try {
|
|
5342
|
+
await client.forget(info.url, apiKey, id);
|
|
5343
|
+
console.log(`Forgot ${id}`);
|
|
5344
|
+
}
|
|
5345
|
+
catch (err) {
|
|
5346
|
+
console.error(err.message);
|
|
5347
|
+
process.exit(1);
|
|
5348
|
+
}
|
|
5349
|
+
});
|
|
5350
|
+
if (routed)
|
|
5351
|
+
break;
|
|
5352
|
+
}
|
|
5353
|
+
cmdForget(hippoRoot, id, flags);
|
|
5255
5354
|
break;
|
|
5256
5355
|
}
|
|
5257
5356
|
case 'inspect': {
|