enigma-memory 0.1.3 → 0.1.5

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.
@@ -36,6 +36,125 @@ const PUBLIC_PLAINTEXT_KEYS = new Set(['body', 'content', 'context', 'contexttex
36
36
  const SOURCE_REF_ROOT_RE = /^sha256:[a-f0-9]{64}$/;
37
37
  const PUBLIC_KEY_REQUIRED_FOR_CONTEXT_PACK_VERIFICATION = 'PUBLIC_KEY_REQUIRED_FOR_CONTEXT_PACK_VERIFICATION';
38
38
 
39
+ const QUERY_RELEVANCE_STOPWORDS = new Set([
40
+ 'about',
41
+ 'after',
42
+ 'again',
43
+ 'against',
44
+ 'also',
45
+ 'and',
46
+ 'any',
47
+ 'are',
48
+ 'assistant',
49
+ 'because',
50
+ 'been',
51
+ 'before',
52
+ 'being',
53
+ 'between',
54
+ 'can',
55
+ 'could',
56
+ 'current',
57
+ 'does',
58
+ 'from',
59
+ 'has',
60
+ 'have',
61
+ 'how',
62
+ 'into',
63
+ 'its',
64
+ 'latest',
65
+ 'more',
66
+ 'most',
67
+ 'number',
68
+ 'own',
69
+ 'owns',
70
+ 'please',
71
+ 'should',
72
+ 'that',
73
+ 'the',
74
+ 'their',
75
+ 'then',
76
+ 'there',
77
+ 'these',
78
+ 'they',
79
+ 'this',
80
+ 'use',
81
+ 'using',
82
+ 'was',
83
+ 'what',
84
+ 'when',
85
+ 'where',
86
+ 'which',
87
+ 'who',
88
+ 'whose',
89
+ 'why',
90
+ 'with',
91
+ 'would',
92
+ ]);
93
+
94
+ function addMeaningfulToken(tokens, token) {
95
+ if (token.length < 3) return;
96
+ if (!/[a-z]/u.test(token)) return;
97
+ if (QUERY_RELEVANCE_STOPWORDS.has(token)) return;
98
+ tokens.add(token);
99
+ }
100
+
101
+ function meaningfulTokensFrom(value) {
102
+ const tokens = new Set();
103
+ if (value === undefined || value === null) return tokens;
104
+ for (const match of String(value).toLowerCase().matchAll(/[a-z0-9]+(?:[-_][a-z0-9]+)*/gu)) {
105
+ const token = match[0];
106
+ addMeaningfulToken(tokens, token);
107
+ if (token.includes('-') || token.includes('_')) {
108
+ for (const part of token.split(/[-_]+/u)) addMeaningfulToken(tokens, part);
109
+ }
110
+ }
111
+ return tokens;
112
+ }
113
+
114
+ function addTokensFromValue(tokens, value) {
115
+ for (const token of meaningfulTokensFrom(value)) tokens.add(token);
116
+ }
117
+
118
+ function candidateRelevanceTokens(candidate) {
119
+ const tokens = new Set();
120
+ addTokensFromValue(tokens, candidate.content);
121
+ addTokensFromValue(tokens, candidate.metadata?.kind);
122
+ for (const tag of candidate.metadata?.purpose_tags ?? []) addTokensFromValue(tokens, tag);
123
+ return tokens;
124
+ }
125
+
126
+ function hasTokenOverlap(left, right) {
127
+ for (const token of left) {
128
+ if (right.has(token)) return true;
129
+ }
130
+ return false;
131
+ }
132
+
133
+ function strictQueryRelevance(args) {
134
+ return args.strict_relevance === true
135
+ || args.strictRelevance === true
136
+ || args.require_relevance === true
137
+ || args.requireRelevance === true
138
+ || args.query_relevance === 'strict'
139
+ || args.queryRelevance === 'strict';
140
+ }
141
+
142
+ function relevanceCandidateSet(args, candidates) {
143
+ if (args.queryAwareRelevance !== true) return candidates;
144
+ const query = typeof args.query === 'string' ? args.query.trim() : String(args.query ?? '').trim();
145
+ if (query.length === 0) return candidates;
146
+ const queryTokens = meaningfulTokensFrom(query);
147
+ if (queryTokens.size === 0) return candidates;
148
+
149
+ const relevant = [];
150
+ for (const candidate of candidates) {
151
+ if (hasTokenOverlap(queryTokens, candidateRelevanceTokens(candidate))) relevant.push(candidate);
152
+ }
153
+ if (relevant.length > 0) return relevant;
154
+ return strictQueryRelevance(args) ? [] : candidates;
155
+ }
156
+
157
+
39
158
  function normalizedPublicKey(key) {
40
159
  return String(key).toLowerCase().replace(/[^a-z0-9]/g, '');
41
160
  }
@@ -215,8 +334,9 @@ function optimizedSelectionFrom(args, candidateAddresses, limit) {
215
334
  const candidate = optimizationCandidateFrom(args.vault, memoryAddr);
216
335
  if (candidate) candidates.push(candidate);
217
336
  }
337
+ const planCandidates = relevanceCandidateSet(args, candidates);
218
338
  const plan = createMemoryOptimizationPlan({
219
- candidates,
339
+ candidates: planCandidates,
220
340
  prompt: args.query ?? '',
221
341
  pricing: contextPackPricing(args),
222
342
  now: args.now,
@@ -237,7 +357,7 @@ function optimizedSelectionFrom(args, candidateAddresses, limit) {
237
357
  }
238
358
  const selectedSet = new Set(selected);
239
359
  const selectedPlan = createMemoryOptimizationPlan({
240
- candidates: candidates.filter((candidate) => selectedSet.has(candidate.address)),
360
+ candidates: planCandidates.filter((candidate) => selectedSet.has(candidate.address)),
241
361
  prompt: args.query ?? '',
242
362
  pricing: contextPackPricing(args),
243
363
  now: args.now,
@@ -503,11 +623,12 @@ export function compileContextPack(args = {}) {
503
623
  const tombstones = tombstoneAddressesFrom(vault);
504
624
  const limit = Number(args.limit ?? args.max_memories ?? args.maxMemories ?? 12);
505
625
  if (!Number.isInteger(limit) || limit < 0) throw new Error('compileContextPack limit must be a non-negative integer');
506
- const candidateAddresses = requested ? [...requested] : [...active];
626
+ const hasExplicitMemoryAddresses = Boolean(requested);
627
+ const candidateAddresses = hasExplicitMemoryAddresses ? [...requested] : [...active];
507
628
  let selected = candidateAddresses.slice(0, limit);
508
629
  let optimizationPlan = null;
509
630
  if (optimizerEnabled(args)) {
510
- const optimized = optimizedSelectionFrom({ ...args, vault }, candidateAddresses, limit);
631
+ const optimized = optimizedSelectionFrom({ ...args, vault, queryAwareRelevance: !hasExplicitMemoryAddresses }, candidateAddresses, limit);
511
632
  selected = optimized.selected;
512
633
  optimizationPlan = optimized.plan;
513
634
  }
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
 
7
7
  export const INSTALLER_ASSET_SCHEMA = 'enigma.installer_assets.v1';
8
8
  export const INSTALLER_ASSET_PACKAGE = 'enigma-memory';
9
- export const INSTALLER_ASSET_VERSION = '0.1.3';
9
+ export const INSTALLER_ASSET_VERSION = '0.1.5';
10
10
  export const INSTALLER_ASSET_GENERATED_AT = '1970-01-01T00:00:00.000Z';
11
11
 
12
12
  const SCRIPT_PATH = fileURLToPath(import.meta.url);
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from 'node:crypto';
3
+ import { createWriteStream as defaultCreateWriteStream } from 'node:fs';
4
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { mkdir as defaultMkdir, writeFile as defaultWriteFile } from 'node:fs/promises';
7
+
8
+ export const STANDARD_BENCHMARK_DATASET_MANIFEST_SCHEMA = 'enigma.standard_benchmark_dataset_manifest.v1';
9
+ export const STANDARD_BENCHMARK_DATASET_PLAN_SCHEMA = 'enigma.standard_benchmark_dataset_download_plan.v1';
10
+ export const DEFAULT_DATASET_DIR = '.enigma/benchmarks/datasets';
11
+ export const DEFAULT_MANIFEST_FILE_NAME = 'standard-benchmark-dataset-manifest.json';
12
+
13
+ export const DATASET_IDS = Object.freeze([
14
+ 'locomo',
15
+ 'longmemeval-oracle',
16
+ 'longmemeval-s',
17
+ 'longmemeval-m',
18
+ ]);
19
+
20
+ export const DATASET_SELECTIONS = Object.freeze([...DATASET_IDS, 'all']);
21
+
22
+ export const STANDARD_BENCHMARK_DATASETS = Object.freeze({
23
+ locomo: Object.freeze({
24
+ id: 'locomo',
25
+ display_name: 'LoCoMo',
26
+ file_name: 'locomo10.json',
27
+ source_url: 'https://raw.githubusercontent.com/snap-research/locomo/main/data/locomo10.json',
28
+ license: 'CC BY-NC 4.0',
29
+ usage_boundaries: Object.freeze([
30
+ 'Official LoCoMo data is non-commercial; review the upstream license before use or redistribution.',
31
+ 'Use as a long-term conversational-memory benchmark source, not as proof of provider deletion, model forgetting, ROI, savings, compliance, or benchmark leadership.',
32
+ 'Public reports must keep raw conversation text out of generated manifests and shared summaries.',
33
+ ]),
34
+ }),
35
+ 'longmemeval-oracle': Object.freeze({
36
+ id: 'longmemeval-oracle',
37
+ display_name: 'LongMemEval Oracle',
38
+ file_name: 'longmemeval_oracle.json',
39
+ source_url: 'https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_oracle.json',
40
+ license: 'Review the upstream Hugging Face dataset card and LongMemEval repository terms before use or redistribution.',
41
+ usage_boundaries: Object.freeze([
42
+ 'Oracle split includes evidence sessions and is useful for retrieval/proxy controls; it is not a live provider comparison by itself.',
43
+ 'LongMemEval covers information extraction, multi-session reasoning, temporal reasoning, knowledge updates, and abstention.',
44
+ 'Public reports must keep raw question, answer, and conversation text out of generated manifests and shared summaries.',
45
+ ]),
46
+ }),
47
+ 'longmemeval-s': Object.freeze({
48
+ id: 'longmemeval-s',
49
+ display_name: 'LongMemEval S cleaned',
50
+ file_name: 'longmemeval_s_cleaned.json',
51
+ source_url: 'https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_s_cleaned.json',
52
+ license: 'Review the upstream Hugging Face dataset card and LongMemEval repository terms before use or redistribution.',
53
+ usage_boundaries: Object.freeze([
54
+ 'Cleaned LongMemEval S is for reproducible benchmark preparation; it is not a live provider comparison by itself.',
55
+ 'LongMemEval covers information extraction, multi-session reasoning, temporal reasoning, knowledge updates, and abstention.',
56
+ 'Public reports must keep raw question, answer, and conversation text out of generated manifests and shared summaries.',
57
+ ]),
58
+ }),
59
+ 'longmemeval-m': Object.freeze({
60
+ id: 'longmemeval-m',
61
+ display_name: 'LongMemEval M cleaned',
62
+ file_name: 'longmemeval_m_cleaned.json',
63
+ source_url: 'https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_m_cleaned.json',
64
+ license: 'Review the upstream Hugging Face dataset card and LongMemEval repository terms before use or redistribution.',
65
+ usage_boundaries: Object.freeze([
66
+ 'Cleaned LongMemEval M is large long-memory benchmark data; it is not a live provider comparison by itself.',
67
+ 'LongMemEval covers information extraction, multi-session reasoning, temporal reasoning, knowledge updates, and abstention.',
68
+ 'Public reports must keep raw question, answer, and conversation text out of generated manifests and shared summaries.',
69
+ ]),
70
+ }),
71
+ });
72
+
73
+ function joinOutputPath(base, fileName) {
74
+ const trimmed = String(base).replace(/[\\/]+$/, '');
75
+ if (!trimmed) {
76
+ return fileName;
77
+ }
78
+ return trimmed.includes('\\') ? join(trimmed, fileName) : `${trimmed}/${fileName}`;
79
+ }
80
+
81
+ export const DEFAULT_DATASET_OUTPUT_FILES = Object.freeze(
82
+ Object.fromEntries(DATASET_IDS.map((id) => [id, joinOutputPath(DEFAULT_DATASET_DIR, STANDARD_BENCHMARK_DATASETS[id].file_name)])),
83
+ );
84
+
85
+ export const STANDARD_BENCHMARK_DATASET_URLS = Object.freeze(
86
+ Object.fromEntries(DATASET_IDS.map((id) => [id, STANDARD_BENCHMARK_DATASETS[id].source_url])),
87
+ );
88
+
89
+ export const STANDARD_BENCHMARK_DATASET_FILE_NAMES = Object.freeze(
90
+ Object.fromEntries(DATASET_IDS.map((id) => [id, STANDARD_BENCHMARK_DATASETS[id].file_name])),
91
+ );
92
+
93
+ export const LONGMEMEVAL_TASK_CATEGORIES = Object.freeze([
94
+ 'information extraction',
95
+ 'multi-session reasoning',
96
+ 'temporal reasoning',
97
+ 'knowledge updates',
98
+ 'abstention',
99
+ ]);
100
+
101
+ function fail(message) {
102
+ throw new Error(message);
103
+ }
104
+
105
+ function takeValue(argv, index, flag) {
106
+ const value = argv[index + 1];
107
+ if (!value || value.startsWith('--')) {
108
+ fail(`${flag} requires a value`);
109
+ }
110
+ return value;
111
+ }
112
+
113
+ export function parseDownloadArgs(argv = process.argv.slice(2)) {
114
+ const options = {
115
+ outDir: DEFAULT_DATASET_DIR,
116
+ dataset: 'all',
117
+ dryRun: true,
118
+ manifestPath: undefined,
119
+ help: false,
120
+ };
121
+
122
+ for (let index = 0; index < argv.length; index += 1) {
123
+ const arg = argv[index];
124
+ if (arg === '--help' || arg === '-h') {
125
+ options.help = true;
126
+ } else if (arg === '--out-dir') {
127
+ options.outDir = takeValue(argv, index, arg);
128
+ index += 1;
129
+ } else if (arg === '--dataset') {
130
+ options.dataset = takeValue(argv, index, arg);
131
+ index += 1;
132
+ } else if (arg === '--dry-run') {
133
+ options.dryRun = true;
134
+ } else if (arg === '--execute') {
135
+ options.dryRun = false;
136
+ } else if (arg === '--manifest') {
137
+ options.manifestPath = takeValue(argv, index, arg);
138
+ index += 1;
139
+ } else {
140
+ fail(`Unknown option: ${arg}`);
141
+ }
142
+ }
143
+
144
+ if (!DATASET_SELECTIONS.includes(options.dataset)) {
145
+ fail(`Unsupported dataset "${options.dataset}". Expected one of: ${DATASET_SELECTIONS.join(', ')}`);
146
+ }
147
+
148
+ return options;
149
+ }
150
+
151
+ export function selectedDatasetIds(selection = 'all') {
152
+ if (!DATASET_SELECTIONS.includes(selection)) {
153
+ fail(`Unsupported dataset "${selection}". Expected one of: ${DATASET_SELECTIONS.join(', ')}`);
154
+ }
155
+ return selection === 'all' ? [...DATASET_IDS] : [selection];
156
+ }
157
+
158
+ export function createDatasetDownloadPlan(options = {}) {
159
+ const outDir = options.outDir ?? DEFAULT_DATASET_DIR;
160
+ const dataset = options.dataset ?? 'all';
161
+ const dryRun = options.dryRun ?? true;
162
+ const manifestPath = options.manifestPath ?? joinOutputPath(outDir, DEFAULT_MANIFEST_FILE_NAME);
163
+ const datasetIds = selectedDatasetIds(dataset);
164
+
165
+ return {
166
+ schema: STANDARD_BENCHMARK_DATASET_PLAN_SCHEMA,
167
+ public_safe: true,
168
+ dry_run: Boolean(dryRun),
169
+ execute_required_for_download: Boolean(dryRun),
170
+ raw_dataset_content_included: false,
171
+ selected_dataset: dataset,
172
+ output_directory: outDir,
173
+ manifest_path: manifestPath,
174
+ planned_fetches: datasetIds.map((id) => {
175
+ const datasetInfo = STANDARD_BENCHMARK_DATASETS[id];
176
+ return {
177
+ dataset: datasetInfo.id,
178
+ display_name: datasetInfo.display_name,
179
+ source_url: datasetInfo.source_url,
180
+ license: datasetInfo.license,
181
+ usage_boundaries: [...datasetInfo.usage_boundaries],
182
+ file_name: datasetInfo.file_name,
183
+ output_file: joinOutputPath(outDir, datasetInfo.file_name),
184
+ content_included: false,
185
+ };
186
+ }),
187
+ };
188
+ }
189
+
190
+ function assertFetchResponse(response, datasetId) {
191
+ if (!response || response.ok === false) {
192
+ const status = response?.status ? ` HTTP ${response.status}` : '';
193
+ fail(`Failed to fetch ${datasetId}.${status}`);
194
+ }
195
+ }
196
+
197
+ function sha256Hex(buffer) {
198
+ return createHash('sha256').update(buffer).digest('hex');
199
+ }
200
+
201
+ function responseHasStreamBody(response) {
202
+ return response?.body
203
+ && (typeof response.body.getReader === 'function' || typeof response.body[Symbol.asyncIterator] === 'function');
204
+ }
205
+
206
+ function normalizeBodyChunk(chunk, datasetId) {
207
+ if (typeof chunk === 'string') {
208
+ return Buffer.from(chunk, 'utf8');
209
+ }
210
+ if (chunk instanceof ArrayBuffer) {
211
+ return new Uint8Array(chunk);
212
+ }
213
+ if (ArrayBuffer.isView(chunk)) {
214
+ return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
215
+ }
216
+ fail(`Fetch response stream for ${datasetId} yielded an unsupported chunk type`);
217
+ }
218
+
219
+ async function waitForWritableEvent(writer, eventName) {
220
+ await new Promise((resolveEvent, rejectEvent) => {
221
+ const cleanup = () => {
222
+ writer.off(eventName, onEvent);
223
+ writer.off('error', onError);
224
+ };
225
+ const onEvent = () => {
226
+ cleanup();
227
+ resolveEvent();
228
+ };
229
+ const onError = (error) => {
230
+ cleanup();
231
+ rejectEvent(error);
232
+ };
233
+
234
+ writer.once(eventName, onEvent);
235
+ writer.once('error', onError);
236
+ });
237
+ }
238
+
239
+ async function writeStreamChunk(writer, chunk) {
240
+ if (!writer.write(chunk)) {
241
+ await waitForWritableEvent(writer, 'drain');
242
+ }
243
+ }
244
+
245
+ async function* responseBodyChunks(body) {
246
+ if (typeof body.getReader === 'function') {
247
+ const reader = body.getReader();
248
+ try {
249
+ while (true) {
250
+ const { done, value } = await reader.read();
251
+ if (done) {
252
+ return;
253
+ }
254
+ yield value;
255
+ }
256
+ } finally {
257
+ reader.releaseLock?.();
258
+ }
259
+ return;
260
+ }
261
+
262
+ yield* body;
263
+ }
264
+
265
+ async function streamResponseToFile(response, outputFile, datasetId, hooks) {
266
+ const createWriteStreamImpl = hooks.createWriteStream ?? defaultCreateWriteStream;
267
+ const writer = createWriteStreamImpl(outputFile);
268
+ const finished = new Promise((resolveFinished, rejectFinished) => {
269
+ writer.once('finish', resolveFinished);
270
+ writer.once('error', rejectFinished);
271
+ });
272
+ finished.catch(() => {});
273
+ const hash = createHash('sha256');
274
+ let byteSize = 0;
275
+
276
+ try {
277
+ for await (const chunk of responseBodyChunks(response.body)) {
278
+ const normalizedChunk = normalizeBodyChunk(chunk, datasetId);
279
+ byteSize += normalizedChunk.byteLength;
280
+ hash.update(normalizedChunk);
281
+ await writeStreamChunk(writer, normalizedChunk);
282
+ }
283
+ writer.end();
284
+ await finished;
285
+ } catch (error) {
286
+ writer.destroy?.(error);
287
+ throw error;
288
+ }
289
+
290
+ return {
291
+ byteSize,
292
+ sha256: hash.digest('hex'),
293
+ };
294
+ }
295
+
296
+ async function responseToBuffer(response, datasetId) {
297
+ assertFetchResponse(response, datasetId);
298
+ if (typeof response.arrayBuffer === 'function') {
299
+ return Buffer.from(await response.arrayBuffer());
300
+ }
301
+ if (typeof response.text === 'function') {
302
+ return Buffer.from(await response.text(), 'utf8');
303
+ }
304
+ fail(`Fetch response for ${datasetId} does not expose body, arrayBuffer() or text()`);
305
+ }
306
+
307
+ export async function executeDatasetDownloadPlan(plan, hooks = {}) {
308
+ const fetchImpl = hooks.fetch ?? globalThis.fetch;
309
+ if (typeof fetchImpl !== 'function') {
310
+ fail('No fetch implementation is available; use Node 24+ or pass a fetch hook.');
311
+ }
312
+
313
+ const mkdirImpl = hooks.mkdir ?? defaultMkdir;
314
+ const writeFileImpl = hooks.writeFile ?? defaultWriteFile;
315
+ const now = hooks.now ?? (() => new Date().toISOString());
316
+ const fetchedAt = now();
317
+ const records = [];
318
+
319
+ for (const fetchPlan of plan.planned_fetches) {
320
+ const response = await fetchImpl(fetchPlan.source_url, { redirect: 'follow' });
321
+ assertFetchResponse(response, fetchPlan.dataset);
322
+ await mkdirImpl(dirname(fetchPlan.output_file), { recursive: true });
323
+
324
+ let downloaded;
325
+ if (responseHasStreamBody(response)) {
326
+ downloaded = await streamResponseToFile(response, fetchPlan.output_file, fetchPlan.dataset, hooks);
327
+ } else {
328
+ const bytes = await responseToBuffer(response, fetchPlan.dataset);
329
+ await writeFileImpl(fetchPlan.output_file, bytes);
330
+ downloaded = {
331
+ byteSize: bytes.byteLength,
332
+ sha256: sha256Hex(bytes),
333
+ };
334
+ }
335
+ records.push({
336
+ dataset: fetchPlan.dataset,
337
+ display_name: fetchPlan.display_name,
338
+ source_url: fetchPlan.source_url,
339
+ license: fetchPlan.license,
340
+ usage_boundaries: fetchPlan.usage_boundaries,
341
+ file_name: fetchPlan.file_name,
342
+ output_file: fetchPlan.output_file,
343
+ byte_size: downloaded.byteSize,
344
+ sha256: downloaded.sha256,
345
+ fetched_at: fetchedAt,
346
+ content_included: false,
347
+ });
348
+ }
349
+
350
+ const manifest = {
351
+ schema: STANDARD_BENCHMARK_DATASET_MANIFEST_SCHEMA,
352
+ public_safe: true,
353
+ raw_dataset_content_included: false,
354
+ generated_at: now(),
355
+ output_directory: plan.output_directory,
356
+ datasets: records,
357
+ claim_boundaries: [
358
+ 'Manifest records downloaded file sizes, checksums, licenses, and source URLs only; it contains no raw dataset records.',
359
+ 'Downloaded datasets support retrieval/evidence-coverage or other reviewed benchmark scoring; they do not create provider, competitor, model-forgetting, deletion, ROI, savings, compliance, or benchmark-leadership claims.',
360
+ 'Provider-key LLM answer scoring is out of scope for this downloader and must be added only with reviewed credentials and scorer boundaries.',
361
+ ],
362
+ };
363
+
364
+ await mkdirImpl(dirname(plan.manifest_path), { recursive: true });
365
+ await writeFileImpl(plan.manifest_path, `${JSON.stringify(manifest, null, 2)}\n`);
366
+ return manifest;
367
+ }
368
+
369
+ export async function runDownloadCommand(options = {}, hooks = {}) {
370
+ const plan = createDatasetDownloadPlan(options);
371
+ if (plan.dry_run) {
372
+ return plan;
373
+ }
374
+ return executeDatasetDownloadPlan(plan, hooks);
375
+ }
376
+
377
+ function usage() {
378
+ return `Usage: node scripts/download-standard-benchmarks.mjs [options]\n\nOptions:\n --out-dir <path> Dataset output directory (default: ${DEFAULT_DATASET_DIR})\n --dataset <name> One of: ${DATASET_SELECTIONS.join(', ')} (default: all)\n --dry-run Print planned public-safe fetches without downloading (default)\n --execute Download selected datasets and write a public-safe manifest\n --manifest <path> Manifest path (default: <out-dir>/${DEFAULT_MANIFEST_FILE_NAME})\n -h, --help Show this help\n`;
379
+ }
380
+
381
+ async function main() {
382
+ const options = parseDownloadArgs();
383
+ if (options.help) {
384
+ process.stdout.write(usage());
385
+ return;
386
+ }
387
+ const result = await runDownloadCommand(options);
388
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
389
+ }
390
+
391
+ const invokedPath = process.argv[1] ? resolve(process.argv[1]) : '';
392
+ const modulePath = fileURLToPath(import.meta.url);
393
+
394
+ if (isAbsolute(invokedPath) && invokedPath === modulePath) {
395
+ main().catch((error) => {
396
+ process.stderr.write(`${error.message}\n`);
397
+ process.exitCode = 1;
398
+ });
399
+ }
@@ -69,6 +69,7 @@ const EXTERNAL_COMPETITOR_ADAPTERS = Object.freeze([
69
69
  'Operator-supplied benchmark dataset or fixture mapping',
70
70
  ],
71
71
  official_doc: 'https://docs.letta.com/concepts/memgpt/',
72
+ official_positioning: 'Letta/MemGPT provides an agent memory runtime; this harness requires operator-supplied credentials/runtime and pinned SDK artifacts before any comparison.',
72
73
  can_run_in_this_harness: false,
73
74
  boundary_reason: 'This local harness has no Letta credentials, SDK installation, hosted/self-hosted Letta runtime, fixed agent loop, or approved external dataset, so no Letta score is produced.',
74
75
  scores_included: false,
@@ -84,6 +85,7 @@ const EXTERNAL_COMPETITOR_ADAPTERS = Object.freeze([
84
85
  'Fixed graph, model, tools, and dataset mapping',
85
86
  ],
86
87
  official_doc: 'https://docs.langchain.com/oss/python/langgraph/memory',
88
+ official_positioning: 'LangGraph memory includes short-term checkpointer memory and a long-term namespaced store; this harness does not execute that runtime.',
87
89
  can_run_in_this_harness: false,
88
90
  boundary_reason: 'This Node local package harness does not install or execute a LangGraph runtime, checkpointer, namespaced store, graph, model, or dataset adapter, so no LangGraph score is produced.',
89
91
  scores_included: false,
@@ -99,6 +101,7 @@ const EXTERNAL_COMPETITOR_ADAPTERS = Object.freeze([
99
101
  'Dataset ingestion and retrieval mapping',
100
102
  ],
101
103
  official_doc: 'https://help.getzep.com/',
104
+ official_positioning: 'Zep positions memory around a temporal Context Graph/Context Lake and sub-200ms retrieval; this harness does not verify that retrieval claim.',
102
105
  can_run_in_this_harness: false,
103
106
  boundary_reason: 'This local harness has no Zep credentials, client package, Context Graph or Context Lake runtime, ingestion job, or provider-approved retrieval dataset, so no Zep score or latency claim is produced.',
104
107
  scores_included: false,
@@ -114,6 +117,7 @@ const EXTERNAL_COMPETITOR_ADAPTERS = Object.freeze([
114
117
  'Dataset ingestion and scoring mapping',
115
118
  ],
116
119
  official_doc: 'https://docs.mem0.ai/',
120
+ official_positioning: 'Mem0 positions itself as a universal self-improving memory layer with platform and open-source stack options; this harness does not run either stack.',
117
121
  can_run_in_this_harness: false,
118
122
  boundary_reason: 'This local harness has no Mem0 credentials, SDK/runtime, configured extraction/retrieval loop, model/tool environment, or external dataset adapter, so no Mem0 score is produced.',
119
123
  scores_included: false,
@@ -129,6 +133,7 @@ const EXTERNAL_COMPETITOR_ADAPTERS = Object.freeze([
129
133
  'Evidence capture that excludes personal data and credentials',
130
134
  ],
131
135
  official_doc: 'https://help.openai.com/en/articles/8590148-memory-faq',
136
+ official_positioning: 'OpenAI ChatGPT native memory is a consumer-app/native capability and is not directly available through this local public-API harness.',
132
137
  can_run_in_this_harness: false,
133
138
  boundary_reason: 'ChatGPT native memory is a consumer-app feature rather than a public API surface available to this local package harness, so no OpenAI native-memory score is produced.',
134
139
  scores_included: false,
@@ -144,6 +149,7 @@ const EXTERNAL_COMPETITOR_ADAPTERS = Object.freeze([
144
149
  'Evidence capture that excludes personal data and credentials',
145
150
  ],
146
151
  official_doc: 'https://support.anthropic.com/en/articles/11145838-using-claude-memory',
152
+ official_positioning: 'Claude memory tooling is provider/client-side and requires a Claude runtime plus tool environment outside this local package harness.',
147
153
  can_run_in_this_harness: false,
148
154
  boundary_reason: 'The Claude memory tool is provider/client-side and requires a Claude runtime plus tool environment that this local package benchmark does not control, so no Claude memory-tool score is produced.',
149
155
  scores_included: false,
@@ -675,6 +681,7 @@ function compareLocalBaselines(vault, passport) {
675
681
 
676
682
  rows.push({
677
683
  id: baseline.id,
684
+ baseline: baseline.id,
678
685
  label: baseline.label,
679
686
  boundary: baseline.boundary,
680
687
  local_fixture_only: true,
@@ -684,6 +691,7 @@ function compareLocalBaselines(vault, passport) {
684
691
  exact_answer_questions: exactTotal,
685
692
  exact_answer_correct: exactCorrect,
686
693
  exact_answer_recall: exactTotal === 0 ? 0 : Number((exactCorrect / exactTotal).toFixed(6)),
694
+ recall: exactTotal === 0 ? 0 : Number((exactCorrect / exactTotal).toFixed(6)),
687
695
  abstention_questions: abstainTotal,
688
696
  abstention_correct: abstainCorrect,
689
697
  abstention_correctness: abstainTotal === 0 ? 0 : Number((abstainCorrect / abstainTotal).toFixed(6)),
@@ -740,6 +748,8 @@ function compareProviders(vault, passport, samples) {
740
748
  provider_runtime_observed: false,
741
749
  external_provider_called: false,
742
750
  same_enigma_context_pack_boundary: true,
751
+ scores_included: false,
752
+ not_external_competitor_score: true,
743
753
  boundary: {
744
754
  optimize: CONTEXT_BOUNDARY.optimize,
745
755
  max_estimated_tokens: CONTEXT_BOUNDARY.max_estimated_tokens,
@@ -863,6 +873,7 @@ export function runMemoryBenchmarkSuite(options = {}) {
863
873
  context_pack_verify_valid: contextVerification.valid === true,
864
874
  },
865
875
  },
876
+ local_baseline_comparisons: localBaselineRows,
866
877
  cross_provider_profiles: providerRows,
867
878
  external_competitor_adapters: EXTERNAL_COMPETITOR_ADAPTERS,
868
879
  public_claims_allowed: PUBLIC_CLAIMS_ALLOWED,