hippo-memory 1.41.0 → 1.42.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.
@@ -1,48 +1,46 @@
1
1
  import { createRequire } from 'node:module';
2
+ import { pathToFileURL } from 'node:url';
2
3
  const MODEL_NAME = 'Xenova/ms-marco-MiniLM-L-6-v2';
3
- // Use Function constructor to bypass TypeScript static module resolution
4
- // for optional peer dependencies that may not be installed (mirrors the
5
- // pattern in src/embeddings.ts).
6
- // SAFETY: this declares the contract we require from the dynamically
7
- // imported package (a `pipeline` factory, top-level or under `default`);
8
- // loadTransformersModule validates the resolved value is callable via
9
- // isPipelineFactory before it is ever invoked.
10
- const _dynImport = new Function('s', 'return import(s)');
11
4
  const _require = createRequire(import.meta.url);
12
5
  const TRANSFORMERS_PACKAGES = ['@huggingface/transformers', '@xenova/transformers'];
6
+ // Returns the ESM entry URL, the same build src/embeddings.ts imports. A
7
+ // require-style resolve picks the CommonJS build and puts a second copy of
8
+ // the library, with its own ONNX sessions, in the process.
13
9
  function resolveTransformersPackage() {
14
10
  for (const name of TRANSFORMERS_PACKAGES) {
15
11
  try {
16
- _require.resolve(name);
17
- return name;
12
+ return import.meta.resolve(name);
18
13
  }
19
14
  catch {
20
- // Try the legacy fallback only when the preferred package is not installed.
15
+ // import.meta.resolve is missing under some module runners (vitest's
16
+ // included); fall through to the require-based resolve there.
17
+ try {
18
+ return pathToFileURL(_require.resolve(name)).href;
19
+ }
20
+ catch {
21
+ // Try the legacy fallback only when the preferred package is not installed.
22
+ }
21
23
  }
22
24
  }
23
25
  return null;
24
26
  }
25
- function isPipelineFactory(value) {
26
- return typeof value === 'function';
27
- }
28
27
  async function loadTransformersModule() {
29
28
  // Import one backend only. Loading both native ONNX runtimes in one process
30
29
  // can abort during finalization; Hugging Face is the maintained default.
31
- const name = resolveTransformersPackage();
32
- if (!name)
30
+ const url = resolveTransformersPackage();
31
+ if (!url)
33
32
  return null;
34
33
  try {
35
- const mod = await _dynImport(name);
36
- const pipeline = mod.pipeline ?? mod.default?.pipeline;
37
- return isPipelineFactory(pipeline)
38
- ? { pipeline }
39
- : null;
34
+ const mod = await import(/* @vite-ignore */ url);
35
+ const tok = mod.AutoTokenizer ?? mod.default?.AutoTokenizer;
36
+ const seq = mod.AutoModelForSequenceClassification ?? mod.default?.AutoModelForSequenceClassification;
37
+ return tok && seq ? { AutoTokenizer: tok, AutoModelForSequenceClassification: seq } : null;
40
38
  }
41
39
  catch {
42
40
  return null;
43
41
  }
44
42
  }
45
- let cachedPipeline = null;
43
+ let pipelineLoading = null;
46
44
  let warnedOnFallback = false;
47
45
  /**
48
46
  * True if a Transformers.js backend is importable. Note: this does NOT confirm
@@ -53,40 +51,55 @@ let warnedOnFallback = false;
53
51
  export async function isCrossEncoderAvailable() {
54
52
  return (await loadTransformersModule()) !== null;
55
53
  }
56
- async function loadPipeline() {
57
- if (cachedPipeline)
58
- return cachedPipeline;
54
+ // NOT the text-classification pipeline: this model is a num_labels=1
55
+ // regression head, and that pipeline softmaxes a length-1 logit vector, which
56
+ // is identically 1.0 for every input. Read the logit, then squash it.
57
+ async function buildPipeline() {
59
58
  try {
60
59
  const mod = await loadTransformersModule();
61
60
  if (!mod)
62
61
  return null;
63
- const p = await mod.pipeline('text-classification', MODEL_NAME);
64
- cachedPipeline = async (query, candidate) => {
65
- const out = await p(`${query} [SEP] ${candidate}`);
66
- return Array.isArray(out) ? out : [out];
62
+ const [tokenizer, model] = await Promise.all([
63
+ mod.AutoTokenizer.from_pretrained(MODEL_NAME),
64
+ mod.AutoModelForSequenceClassification.from_pretrained(MODEL_NAME),
65
+ ]);
66
+ return async (query, candidate) => {
67
+ const inputs = await tokenizer(query, {
68
+ text_pair: candidate,
69
+ padding: true,
70
+ truncation: true,
71
+ });
72
+ const { logits } = await model(inputs);
73
+ const score = 1 / (1 + Math.exp(-Number(logits.data[0])));
74
+ // NaN would make the sort comparator a no-op; throwing hands this
75
+ // candidate to the per-candidate fallback instead.
76
+ if (!Number.isFinite(score))
77
+ throw new Error('cross-encoder returned a non-finite score');
78
+ return score;
67
79
  };
68
- return cachedPipeline;
69
80
  }
70
81
  catch {
71
82
  return null;
72
83
  }
73
84
  }
74
- /**
75
- * Track 2 reranker: MS-MARCO MiniLM cross-encoder.
76
- * Loads model on first call, then sub-100ms per query for top-K=50 candidates
77
- * on a typical developer laptop CPU. Falls back to identity ordering if the
78
- * model fails to load (no transformers, no network for first download, etc.).
79
- *
80
- * See docs/plans/2026-05-10-f6-reranker-hardening.md Task 6.
81
- */
85
+ // One shared in-flight load: two first calls must not fetch the model twice.
86
+ // A failed load clears the slot so a later call can try again.
87
+ function loadPipeline() {
88
+ pipelineLoading ??= buildPipeline().then((pipe) => {
89
+ if (!pipe)
90
+ pipelineLoading = null;
91
+ return pipe;
92
+ });
93
+ return pipelineLoading;
94
+ }
95
+ /** Track 2 reranker: MS-MARCO MiniLM cross-encoder, identity fallback if the model will not load. */
82
96
  export const crossEncoderReranker = async (query, results, options) => {
83
97
  const topK = options?.topK ?? 50;
84
98
  const head = results.slice(0, topK);
85
99
  const pipe = await loadPipeline();
86
100
  if (!pipe) {
87
- // Fallback: identity ordering with rerankScore = original score. Warn
88
- // once per process so a silent identity-fallback doesn't mislead users
89
- // into thinking the cross-encoder is doing work it isn't.
101
+ // Warn once per process: a silent identity fallback otherwise reads as a
102
+ // working reranker.
90
103
  if (!warnedOnFallback) {
91
104
  warnedOnFallback = true;
92
105
  // eslint-disable-next-line no-console
@@ -102,13 +115,10 @@ export const crossEncoderReranker = async (query, results, options) => {
102
115
  const scored = await Promise.all(head.map(async (r, i) => {
103
116
  let ceScore;
104
117
  try {
105
- const out = await pipe(query, r.entry.content);
106
- ceScore = Array.isArray(out) && out.length > 0 ? out[0].score : 0;
118
+ ceScore = await pipe(query, r.entry.content);
107
119
  }
108
120
  catch {
109
- // Per-call inference failure (transient tensor error, bad input,
110
- // etc.): fall back to original score for this candidate so a
111
- // single bad inference doesn't sink the whole rerank pass.
121
+ // One bad inference must not sink the whole pass.
112
122
  ceScore = r.score;
113
123
  }
114
124
  return {
@@ -118,12 +128,8 @@ export const crossEncoderReranker = async (query, results, options) => {
118
128
  postRerankRank: 0,
119
129
  };
120
130
  }));
121
- // T2 note: PLAIN stable score sort on purpose. The input `head` is already
122
- // deterministically ordered (upstream content tail), so stability inherits
123
- // that -- and when the cross-encoder produces tied scores (degenerate or
124
- // no-signal cases), ties MUST fall back to the prior relevance order, not
125
- // an arbitrary content order (the reranker-cross-encoder micro fixture
126
- // fails otherwise: an all-tie rerank pass reordered its input).
131
+ // Plain stable sort on purpose: tied scores MUST fall back to the prior
132
+ // relevance order, never an arbitrary content order.
127
133
  scored.sort((a, b) => b.rerankScore - a.rerankScore);
128
134
  scored.forEach((r, i) => (r.postRerankRank = i + 1));
129
135
  return scored;
@@ -1 +1 @@
1
- {"version":3,"file":"cross-encoder.js","sourceRoot":"","sources":["../../../src/rerankers/cross-encoder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cross-encoder.js","sourceRoot":"","sources":["../../../src/rerankers/cross-encoder.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  import { crossEncoderReranker } from './cross-encoder.js';
2
+ import { jevReranker } from './jev.js';
2
3
  import { llmReranker } from './llm.js';
3
4
  const REGISTRY = {
4
5
  'cross-encoder': crossEncoderReranker,
6
+ jev: jevReranker,
5
7
  llm: llmReranker,
6
8
  };
7
9
  function isRegisteredRerankerName(name) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rerankers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,QAAQ,GAAG;IACf,eAAe,EAAE,oBAAoB;IACrC,GAAG,EAAE,WAAW;CACoB,CAAC;AAIvC,SAAS,wBAAwB,CAAC,IAAY;IAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAA+B;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,gBAAgB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rerankers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,QAAQ,GAAG;IACf,eAAe,EAAE,oBAAoB;IACrC,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;CACoB,CAAC;AAIvC,SAAS,wBAAwB,CAAC,IAAY;IAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAA+B;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,gBAAgB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { crossEncoderReranker } from './cross-encoder.js';
2
+ const ENDPOINT = 'https://api.typesafe.ai/v1/systemone';
3
+ const DEFAULT_TIMEOUT_MS = 5_000;
4
+ const TRUNCATE_CHARS = 1200;
5
+ // The pool size every number in docs/evals/2026-09-19-jev-reranker.md was measured at.
6
+ export const JEV_DEFAULT_TOP_K = 40;
7
+ function truncate(s, n) {
8
+ return s.length <= n ? s : `${s.slice(0, n)}...`;
9
+ }
10
+ // Number.isFinite rejects a string or null without coercing it, which the
11
+ // declared type cannot promise about a third-party payload.
12
+ function isProbability(v) {
13
+ return v !== undefined && Number.isFinite(v) && v >= 0 && v <= 1;
14
+ }
15
+ // A half-scored list ranks worse than the order it would replace, so one bad
16
+ // answer voids the whole response.
17
+ function parseScores(answers, n) {
18
+ const out = [];
19
+ for (let i = 1; i <= n; i++) {
20
+ const v = answers?.[`c${i}`]?.noul;
21
+ if (!isProbability(v))
22
+ return null;
23
+ out.push(v);
24
+ }
25
+ return out;
26
+ }
27
+ /** One batched request for the whole candidate list. Rejects with the reason when there are no usable scores. */
28
+ async function requestScores(query, head) {
29
+ const key = process.env.TYPESAFE_API_KEY;
30
+ if (!key)
31
+ throw new Error('TYPESAFE_API_KEY not set');
32
+ const lines = head.map((r, i) => `[${i + 1}] ${truncate(r.entry.content, TRUNCATE_CHARS)}`);
33
+ const state = `Query: ${query}\n\nNumbered candidate memories from an AI coding agent's project store:\n\n${lines.join('\n\n')}`;
34
+ const questions = {};
35
+ for (let i = 1; i <= head.length; i++) {
36
+ questions[`c${i}`] = {
37
+ type: 'noul',
38
+ instructions: `Probability that candidate ${i} (numbered in the state above) helps answer the query.`,
39
+ };
40
+ }
41
+ const parsed = Number.parseInt(process.env.HIPPO_JEV_TIMEOUT_MS ?? '', 10);
42
+ const timeoutMs = parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS;
43
+ const controller = new AbortController();
44
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
45
+ try {
46
+ const resp = await fetch(ENDPOINT, {
47
+ method: 'POST',
48
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${key}` },
49
+ body: JSON.stringify({
50
+ state,
51
+ model: process.env.HIPPO_JEV_MODEL ?? 'jev-latest',
52
+ questions,
53
+ }),
54
+ signal: controller.signal,
55
+ });
56
+ if (!resp.ok) {
57
+ // A third-party header ends up on stderr, so keep printable ASCII only.
58
+ const requestId = resp.headers.get('x-request-id')?.replace(/[^\x20-\x7e]/g, '').slice(0, 64);
59
+ await resp.body?.cancel();
60
+ throw new Error(`HTTP ${resp.status}${requestId ? `, request ${requestId}` : ''}`);
61
+ }
62
+ const body = await resp.json();
63
+ const scores = parseScores(body.answers, head.length);
64
+ if (!scores)
65
+ throw new Error('incomplete or out-of-range answers');
66
+ return scores;
67
+ }
68
+ catch (err) {
69
+ if (err instanceof Error && err.name === 'AbortError') {
70
+ throw new Error(`no answer within ${timeoutMs} ms`);
71
+ }
72
+ throw err;
73
+ }
74
+ finally {
75
+ clearTimeout(timer);
76
+ }
77
+ }
78
+ /** Builds a Jev reranker around the reranker it degrades to. Exported so a test can pass a stand-in. */
79
+ export function createJevReranker(localFallback) {
80
+ let warned = false;
81
+ return async (query, results, options) => {
82
+ const head = results.slice(0, options?.topK ?? JEV_DEFAULT_TOP_K);
83
+ if (head.length === 0)
84
+ return [];
85
+ let scores;
86
+ try {
87
+ scores = await requestScores(query, head);
88
+ }
89
+ catch (err) {
90
+ // A read path never throws, and never hands back the unranked order
91
+ // without saying so: warn once, then use the free local reranker.
92
+ if (!warned) {
93
+ warned = true;
94
+ const reason = err instanceof Error ? err.message : 'unknown error';
95
+ // eslint-disable-next-line no-console
96
+ console.warn(`[hippo] jev reranker unavailable (${reason}); falling back to the local cross-encoder. Subsequent calls will not repeat this warning.`);
97
+ }
98
+ return localFallback(query, head, options);
99
+ }
100
+ const scored = head.map((r, i) => ({
101
+ ...r,
102
+ rerankScore: scores[i],
103
+ preRerankRank: r.preRerankRank ?? i + 1,
104
+ postRerankRank: 0,
105
+ }));
106
+ // Stable sort: ties fall back to the prior relevance order.
107
+ scored.sort((a, b) => b.rerankScore - a.rerankScore);
108
+ scored.forEach((r, i) => (r.postRerankRank = i + 1));
109
+ return scored;
110
+ };
111
+ }
112
+ /** Track 4 reranker: hosted TypeSafe Jev, opt-in and paid (TYPESAFE_API_KEY), one batched call per recall.
113
+ * Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run.
114
+ * Cost, env vars, evidence and limits: docs/evals/2026-09-19-jev-reranker.md. */
115
+ export const jevReranker = createJevReranker(crossEncoderReranker);
116
+ //# sourceMappingURL=jev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jev.js","sourceRoot":"","sources":["../../../src/rerankers/jev.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@
18
18
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
19
19
  * any packager that drops .json files.
20
20
  */
21
- export const PACKAGE_VERSION = '1.41.0';
21
+ export const PACKAGE_VERSION = '1.42.0';
22
22
  // Bump on every release alongside the 4 other manifests + lockfile.
23
23
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
24
24
  export function compareSemver(a, b) {
package/dist/version.d.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
19
19
  * any packager that drops .json files.
20
20
  */
21
- export declare const PACKAGE_VERSION = "1.41.0";
21
+ export declare const PACKAGE_VERSION = "1.42.0";
22
22
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
23
23
  export declare function compareSemver(a: string, b: string): number;
24
24
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -18,7 +18,7 @@
18
18
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
19
19
  * any packager that drops .json files.
20
20
  */
21
- export const PACKAGE_VERSION = '1.41.0';
21
+ export const PACKAGE_VERSION = '1.42.0';
22
22
  // Bump on every release alongside the 4 other manifests + lockfile.
23
23
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
24
24
  export function compareSemver(a, b) {
@@ -2,7 +2,7 @@
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
4
  "description": "Biologically-inspired memory for AI agents. Decay by default, retrieval strengthening, sleep consolidation.",
5
- "version": "1.41.0",
5
+ "version": "1.42.0",
6
6
 
7
7
  "configSchema": {
8
8
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippo-memory",
3
- "version": "1.41.0",
3
+ "version": "1.42.0",
4
4
  "type": "module",
5
5
  "description": "Hippo Memory plugin for OpenClaw - biologically-inspired agent memory",
6
6
  "main": "index.ts",
@@ -2,7 +2,7 @@
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
4
  "description": "Biologically-inspired memory for AI agents. Decay by default, retrieval strengthening, sleep consolidation.",
5
- "version": "1.41.0",
5
+ "version": "1.42.0",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippo-memory",
3
- "version": "1.41.0",
3
+ "version": "1.42.0",
4
4
  "description": "Biologically-inspired memory system for AI agents. Decay by default, strength through use.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",