hippo-memory 1.9.3 → 1.10.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 +5 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +80 -22
- package/dist/cli.js.map +1 -1
- 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.map +1 -1
- package/dist/search.js +11 -17
- package/dist/search.js.map +1 -1
- package/dist/server-detect.d.ts +50 -4
- package/dist/server-detect.d.ts.map +1 -1
- package/dist/server-detect.js +164 -5
- package/dist/server-detect.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -3
- package/dist/server.js.map +1 -1
- package/dist/src/cli.js +80 -22
- package/dist/src/cli.js.map +1 -1
- package/dist/src/rrf.js +61 -0
- package/dist/src/rrf.js.map +1 -0
- package/dist/src/search.js +11 -17
- package/dist/src/search.js.map +1 -1
- package/dist/src/server-detect.js +164 -5
- package/dist/src/server-detect.js.map +1 -1
- package/dist/src/server.js +23 -3
- package/dist/src/server.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.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 +1 -1
package/README.md
CHANGED
|
@@ -85,6 +85,11 @@ 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
|
+
|
|
88
93
|
### What's new in v1.9.3
|
|
89
94
|
|
|
90
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.
|
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>"
|
|
@@ -57,7 +57,7 @@ import { buildProvenanceCoverage } from './provenance-coverage.js';
|
|
|
57
57
|
import { buildCorrectionLatency } from './correction-latency.js';
|
|
58
58
|
import * as api from './api.js';
|
|
59
59
|
import * as client from './client.js';
|
|
60
|
-
import { detectServer,
|
|
60
|
+
import { detectServer, removePidfileIfOwned } from './server-detect.js';
|
|
61
61
|
import { resolveTenantId } from './tenant.js';
|
|
62
62
|
import { runEval, bootstrapCorpus, compareSummaries } from './eval.js';
|
|
63
63
|
import { runFeatureEval, formatResult, resultToBaseline, detectRegressions } from './eval-suite.js';
|
|
@@ -117,6 +117,19 @@ function requireInit(hippoRoot) {
|
|
|
117
117
|
process.exit(1);
|
|
118
118
|
}
|
|
119
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
|
+
}
|
|
120
133
|
/**
|
|
121
134
|
* Run an HTTP-routed command if a `hippo serve` instance is detected for
|
|
122
135
|
* `hippoRoot`. Returns:
|
|
@@ -124,14 +137,20 @@ function requireInit(hippoRoot) {
|
|
|
124
137
|
* was already surfaced to stdout/stderr by `httpFn`),
|
|
125
138
|
* - false if no server was detected, or if the detected pidfile turned out
|
|
126
139
|
* to be stale (connection refused). On stale, the pidfile is removed
|
|
127
|
-
*
|
|
140
|
+
* if it still names that dead server (a newer one may have replaced
|
|
141
|
+
* it) and the caller should fall back to the direct path.
|
|
128
142
|
*
|
|
129
143
|
* Per the A1 plan footgun #1: stale pidfiles must self-heal, not crash.
|
|
144
|
+
* H2: when HIPPO_REQUIRE_SERVER is set, both fallback paths throw instead of
|
|
145
|
+
* returning false, so a missing server fails loudly rather than silently
|
|
146
|
+
* degrading to direct mode.
|
|
130
147
|
*/
|
|
131
148
|
async function runViaServerIfAvailable(hippoRoot, httpFn) {
|
|
132
|
-
const info = detectServer(hippoRoot);
|
|
133
|
-
if (!info)
|
|
149
|
+
const info = await detectServer(hippoRoot);
|
|
150
|
+
if (!info) {
|
|
151
|
+
failIfServerRequired('no running server was detected for this hippoRoot');
|
|
134
152
|
return false;
|
|
153
|
+
}
|
|
135
154
|
const apiKey = process.env['HIPPO_API_KEY'];
|
|
136
155
|
try {
|
|
137
156
|
await httpFn(info, apiKey);
|
|
@@ -139,8 +158,11 @@ async function runViaServerIfAvailable(hippoRoot, httpFn) {
|
|
|
139
158
|
}
|
|
140
159
|
catch (err) {
|
|
141
160
|
if (client.isConnectionRefused(err)) {
|
|
161
|
+
failIfServerRequired('the server pidfile was stale (connection refused)');
|
|
142
162
|
console.error('hippo: stale server pidfile detected, falling back to direct mode');
|
|
143
|
-
|
|
163
|
+
// Clear the pidfile only if it still names the dead server we just
|
|
164
|
+
// probed — a newer server may have rewritten it (removePidfileIfOwned).
|
|
165
|
+
removePidfileIfOwned(hippoRoot, { pid: info.pid, startedAt: info.started_at });
|
|
144
166
|
return false;
|
|
145
167
|
}
|
|
146
168
|
throw err;
|
|
@@ -2363,20 +2385,49 @@ function cmdOutcome(hippoRoot, flags) {
|
|
|
2363
2385
|
}
|
|
2364
2386
|
console.log(`Applied ${good ? 'positive' : 'negative'} outcome to ${updated} memor${updated === 1 ? 'y' : 'ies'}`);
|
|
2365
2387
|
}
|
|
2366
|
-
function cmdForget(hippoRoot, id) {
|
|
2388
|
+
function cmdForget(hippoRoot, id, flags) {
|
|
2367
2389
|
requireInit(hippoRoot);
|
|
2368
2390
|
const ctx = {
|
|
2369
2391
|
hippoRoot,
|
|
2370
2392
|
tenantId: resolveTenantId({}),
|
|
2371
2393
|
actor: 'cli',
|
|
2372
2394
|
};
|
|
2395
|
+
// A3: raw memories (Slack / GitHub connector ingestion) are append-only — a
|
|
2396
|
+
// BEFORE-DELETE trigger aborts any delete. archiveRaw is the sanctioned
|
|
2397
|
+
// removal path; it records ctx.actor as the archiver for provenance.
|
|
2398
|
+
if (flags['archive'] === true) {
|
|
2399
|
+
const reason = typeof flags['reason'] === 'string' ? flags['reason'] : null;
|
|
2400
|
+
if (!reason) {
|
|
2401
|
+
console.error('hippo forget --archive requires --reason "<why>" (recorded on the archive).');
|
|
2402
|
+
process.exit(1);
|
|
2403
|
+
}
|
|
2404
|
+
try {
|
|
2405
|
+
api.archiveRaw(ctx, id, reason);
|
|
2406
|
+
updateStats(hippoRoot, { forgotten: 1 });
|
|
2407
|
+
console.log(`Archived ${id}`);
|
|
2408
|
+
}
|
|
2409
|
+
catch (err) {
|
|
2410
|
+
console.error(`Could not archive ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2411
|
+
process.exit(1);
|
|
2412
|
+
}
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2373
2415
|
try {
|
|
2374
2416
|
api.forget(ctx, id);
|
|
2375
2417
|
updateStats(hippoRoot, { forgotten: 1 });
|
|
2376
2418
|
console.log(`Forgot ${id}`);
|
|
2377
2419
|
}
|
|
2378
|
-
catch {
|
|
2379
|
-
|
|
2420
|
+
catch (err) {
|
|
2421
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2422
|
+
if (/append-only/i.test(msg)) {
|
|
2423
|
+
// The delete was refused by the append-only trigger — this is a raw
|
|
2424
|
+
// memory, not a missing one. Point the user at the archive path.
|
|
2425
|
+
console.error(`Cannot forget ${id}: it is a raw, append-only memory. ` +
|
|
2426
|
+
`Archive it instead: hippo forget ${id} --archive --reason "<why>"`);
|
|
2427
|
+
}
|
|
2428
|
+
else {
|
|
2429
|
+
console.error(`Memory not found: ${id}`);
|
|
2430
|
+
}
|
|
2380
2431
|
process.exit(1);
|
|
2381
2432
|
}
|
|
2382
2433
|
}
|
|
@@ -4841,6 +4892,8 @@ Commands:
|
|
|
4841
4892
|
current show Active task + recent session events (default)
|
|
4842
4893
|
--json Output as JSON
|
|
4843
4894
|
forget <id> Force remove a memory
|
|
4895
|
+
--archive Archive a raw (append-only) memory instead of deleting
|
|
4896
|
+
--reason "<why>" Reason recorded on the archive (required with --archive)
|
|
4844
4897
|
inspect <id> Show full memory detail
|
|
4845
4898
|
embed Embed all memories for semantic search
|
|
4846
4899
|
--status Show embedding coverage
|
|
@@ -5283,19 +5336,24 @@ async function main() {
|
|
|
5283
5336
|
console.error('Please provide a memory ID.');
|
|
5284
5337
|
process.exit(1);
|
|
5285
5338
|
}
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5339
|
+
// A3: --archive is a direct-DB operation (raw archival via archiveRaw);
|
|
5340
|
+
// the HTTP forget route does not carry it, so archive requests always
|
|
5341
|
+
// take the direct path rather than silently no-op'ing over a server.
|
|
5342
|
+
if (flags['archive'] !== true) {
|
|
5343
|
+
const routed = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
5344
|
+
try {
|
|
5345
|
+
await client.forget(info.url, apiKey, id);
|
|
5346
|
+
console.log(`Forgot ${id}`);
|
|
5347
|
+
}
|
|
5348
|
+
catch (err) {
|
|
5349
|
+
console.error(err.message);
|
|
5350
|
+
process.exit(1);
|
|
5351
|
+
}
|
|
5352
|
+
});
|
|
5353
|
+
if (routed)
|
|
5354
|
+
break;
|
|
5355
|
+
}
|
|
5356
|
+
cmdForget(hippoRoot, id, flags);
|
|
5299
5357
|
break;
|
|
5300
5358
|
}
|
|
5301
5359
|
case 'inspect': {
|