@hasna/mementos 0.14.72 → 0.14.74

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 +1 @@
1
- {"version":3,"file":"memory-cmd-crud.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/memory-cmd-crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAka3D"}
1
+ {"version":3,"file":"memory-cmd-crud.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/memory-cmd-crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwf3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsUzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8UzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D"}
@@ -0,0 +1,13 @@
1
+ export declare const EXIT_USAGE = 64;
2
+ /**
3
+ * Map a CommanderError onto this CLI's exit-status contract.
4
+ *
5
+ * Pure and total: given the `code`/`exitCode` pair Commander produces, returns
6
+ * the status the process should exit with. Success paths (`--help`, `--version`,
7
+ * which arrive with exitCode 0) are passed through untouched.
8
+ */
9
+ export declare function resolveExitCode(err: {
10
+ code?: string;
11
+ exitCode?: number;
12
+ }): number;
13
+ //# sourceMappingURL=exit-codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exit-codes.d.ts","sourceRoot":"","sources":["../../src/cli/exit-codes.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,UAAU,KAAK,CAAC;AA2B7B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAOT"}
@@ -6,7 +6,7 @@ export declare function importanceColor(importance: number): (s: string) => stri
6
6
  export declare function colorScope(scope: MemoryScope): string;
7
7
  export declare function colorCategory(category: MemoryCategory): string;
8
8
  export declare function colorImportance(importance: number): string;
9
- export declare function colorEntityType(type: string): string;
9
+ export declare function colorEntityType(type: string, suffix?: string): string;
10
10
  export declare function resolveEntityArg(nameOrId: string, type?: EntityType): Entity;
11
11
  export declare function outputJson(data: unknown): void;
12
12
  export declare const DEFAULT_COMPACT_LIMIT = 20;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -3010,6 +3010,14 @@ function getApiModeEnvSources() {
3010
3010
  databaseUrlKey: firstEnvKey(DATABASE_URL_ENV_KEYS)
3011
3011
  };
3012
3012
  }
3013
+ function getConfiguredApiEnv() {
3014
+ const rawBase = firstEnv(API_URL_ENV_KEYS);
3015
+ return {
3016
+ baseUrl: rawBase ? normalizeBase(rawBase) : null,
3017
+ apiKeyPresent: Boolean(firstEnvKey(API_KEY_ENV_KEYS)),
3018
+ dbPathKey: firstEnvKey(DB_PATH_ENV_KEYS)
3019
+ };
3020
+ }
3013
3021
  function isLoopbackHost(rawHost) {
3014
3022
  const host = rawHost.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
3015
3023
  return host === "localhost" || host === "::1" || /^127\./.test(host);
@@ -3055,6 +3063,8 @@ function assertUnambiguousStoreEnv() {
3055
3063
  }
3056
3064
  function getApiConfig() {
3057
3065
  assertUnambiguousStoreEnv();
3066
+ if (firstEnvKey(DB_PATH_ENV_KEYS))
3067
+ return null;
3058
3068
  const rawBase = firstEnv(API_URL_ENV_KEYS);
3059
3069
  const apiKey = firstEnv(API_KEY_ENV_KEYS);
3060
3070
  if (!rawBase || !apiKey)
@@ -4179,9 +4189,10 @@ function migrateGlobalDir() {
4179
4189
  }
4180
4190
  }
4181
4191
  function getDbPath() {
4182
- const envPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
4183
- if (envPath) {
4184
- return envPath;
4192
+ for (const key of DB_PATH_ENV_KEYS) {
4193
+ const envPath = process.env[key]?.trim();
4194
+ if (envPath)
4195
+ return envPath;
4185
4196
  }
4186
4197
  const cwd = process.cwd();
4187
4198
  const nearest = findNearestMementosDb(cwd);
@@ -4225,6 +4236,12 @@ function getDatabase(dbPath) {
4225
4236
  if (getStorageMode() === "cloud") {
4226
4237
  return getCloudDatabase();
4227
4238
  }
4239
+ if (process.env["NODE_ENV"] === "test") {
4240
+ const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
4241
+ if (!hasExplicitDbPath) {
4242
+ throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
4243
+ }
4244
+ }
4228
4245
  }
4229
4246
  const path = dbPath || getDbPath();
4230
4247
  if (_db) {
@@ -6036,9 +6053,9 @@ function colorCategory(category) {
6036
6053
  function colorImportance(importance) {
6037
6054
  return importanceColor(importance)(String(importance));
6038
6055
  }
6039
- function colorEntityType(type) {
6056
+ function colorEntityType(type, suffix = "") {
6040
6057
  const colorFn = entityTypeColor[type] || chalk.white;
6041
- return colorFn(type);
6058
+ return colorFn(`${type}${suffix}`);
6042
6059
  }
6043
6060
  function resolveEntityArg(nameOrId, type) {
6044
6061
  const byName = getEntityByName(nameOrId, type);
@@ -59025,6 +59042,26 @@ function applyGlobalOptions(program2) {
59025
59042
  return program2;
59026
59043
  }
59027
59044
 
59045
+ // src/cli/exit-codes.ts
59046
+ var EXIT_USAGE = 64;
59047
+ var USAGE_ERROR_CODES = new Set([
59048
+ "commander.unknownCommand",
59049
+ "commander.unknownOption",
59050
+ "commander.missingArgument",
59051
+ "commander.optionMissingArgument",
59052
+ "commander.missingMandatoryOptionValue",
59053
+ "commander.excessArguments",
59054
+ "commander.conflictingOption",
59055
+ "commander.invalidArgument",
59056
+ "commander.help"
59057
+ ]);
59058
+ function resolveExitCode(err) {
59059
+ const exitCode = typeof err.exitCode === "number" ? err.exitCode : 1;
59060
+ if (exitCode === 0)
59061
+ return 0;
59062
+ return USAGE_ERROR_CODES.has(err.code ?? "") ? EXIT_USAGE : exitCode;
59063
+ }
59064
+
59028
59065
  // node_modules/@hasna/events/dist/commander.js
59029
59066
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
59030
59067
  import { existsSync as existsSync3 } from "fs";
@@ -59827,7 +59864,13 @@ function registerCrudCommands(program2) {
59827
59864
  let resolvedAgentId;
59828
59865
  if (globalOpts.agent) {
59829
59866
  const ag = getAgent(globalOpts.agent);
59830
- resolvedAgentId = ag?.id;
59867
+ if (!ag) {
59868
+ throw new Error(`Unknown agent "${globalOpts.agent}": no registered agent matches that name or id.
59869
+ ` + `Refusing to save. Dropping the flag would write this memory to the unowned ` + `(no-agent) row for key "${key}", overwriting whatever is there under an owner ` + `you did not name.
59870
+ ` + `Register the agent first: mementos register-agent ${globalOpts.agent}
59871
+ ` + `Or omit --agent to write to the unowned row deliberately.`);
59872
+ }
59873
+ resolvedAgentId = ag.id;
59831
59874
  }
59832
59875
  const input = {
59833
59876
  key,
@@ -59843,9 +59886,18 @@ function registerCrudCommands(program2) {
59843
59886
  session_id: globalOpts.session
59844
59887
  };
59845
59888
  if (globalOpts.project) {
59846
- const project = getProject(resolve3(globalOpts.project));
59847
- if (project)
59848
- input.project_id = project.id;
59889
+ const projectPath = resolve3(globalOpts.project);
59890
+ const project = getProject(projectPath);
59891
+ if (!project) {
59892
+ throw new Error(`Unknown project "${projectPath}": no registered project matches that path.
59893
+ ` + `Refusing to save. Dropping the flag would write this memory to the no-project ` + `row for key "${key}", overwriting whatever is there in a scope you did not name.
59894
+ ` + `Register it first: mementos projects --add --path ${projectPath} --name <name>
59895
+ ` + `Or omit --project to write outside any project deliberately.`);
59896
+ }
59897
+ input.project_id = project.id;
59898
+ }
59899
+ if (typeof input.session_id === "string" && MEMORY_SCOPES.includes(input.session_id)) {
59900
+ console.error(chalk2.yellow(`Warning: --session "${input.session_id}" looks like a scope, not a session id. ` + `Note that -s is the global --session, not --scope; this save is writing ` + `session_id="${input.session_id}" and leaving scope at "${input.scope ?? "private"}". ` + `If you meant the scope, pass --scope ${input.session_id} (long form only).`));
59849
59901
  }
59850
59902
  const bucket = (m) => [m.scope ?? "private", m.agent_id ?? "", m.project_id ?? "", m.session_id ?? ""].join("\x1F");
59851
59903
  const targetBucket = bucket({
@@ -59864,9 +59916,9 @@ function registerCrudCommands(program2) {
59864
59916
  if (!match && sameKey.length > 0) {
59865
59917
  const rows = sameKey.map((m) => ` ${m.id.slice(0, 8)} scope=${m.scope} project=${m.project_id ?? "none"} session=${m.session_id ?? "none"} agent=${m.agent_id ?? "none"}`).join(`
59866
59918
  `);
59867
- throw new Error(`Refusing to fork key "${key}": ${sameKey.length} active memor${sameKey.length === 1 ? "y" : "ies"} ` + `already ${sameKey.length === 1 ? "uses" : "use"} it, ` + `but none matches the scope/project/session this save targets ` + `(scope=${input.scope ?? "private"}, project=${input.project_id ?? "none"}, session=${input.session_id ?? "none"}).
59919
+ throw new Error(`Refusing to fork key "${key}": ${sameKey.length} active memor${sameKey.length === 1 ? "y" : "ies"} ` + `already ${sameKey.length === 1 ? "uses" : "use"} it, ` + `but none matches the scope/project/session/agent this save targets ` + `(scope=${input.scope ?? "private"}, project=${input.project_id ?? "none"}, session=${input.session_id ?? "none"}, agent=${input.agent_id ?? "none"}).
59868
59920
  ` + `${rows}
59869
- ` + `Saving would create a second active row under the same key. Either target the existing row ` + `(match its scope/project/session flags, or use \`mementos update <id>\`), or pass \`--dedupe create\` ` + `to fork deliberately.`);
59921
+ ` + `Saving would create a second active row under the same key. Either target the existing row ` + `(match its scope/project/session/agent flags, or use \`mementos update <id>\`), or pass \`--dedupe create\` ` + `to fork deliberately.`);
59870
59922
  }
59871
59923
  }
59872
59924
  const memory = forkRequested ? createMemory(input, dedupe) : createMemory(input);
@@ -60437,7 +60489,7 @@ function registerChainCommand(program2) {
60437
60489
  const m = memories[i];
60438
60490
  const order = m.sequence_order !== null && m.sequence_order !== undefined ? m.sequence_order : i + 1;
60439
60491
  const value = m.value.length > 120 ? m.value.slice(0, 120) + "..." : m.value;
60440
- console.log(` ${chalk8.cyan(String(order) + ".")} ${chalk8.bold(m.key)}: ${value}`);
60492
+ console.log(` ${chalk8.cyan(String(order) + ".")} ${chalk8.bold(`${m.key}:`)} ${value}`);
60441
60493
  }
60442
60494
  } catch (e) {
60443
60495
  handleError(e);
@@ -62977,12 +63029,12 @@ function registerGraphCommands(program2) {
62977
63029
  console.log();
62978
63030
  console.log(chalk26.bold(`Entities: ${stats.entities.total}`));
62979
63031
  for (const r of entityRows) {
62980
- console.log(` ${colorEntityType(r.type)}: ${r.count}`);
63032
+ console.log(` ${colorEntityType(r.type, ":")} ${r.count}`);
62981
63033
  }
62982
63034
  console.log();
62983
63035
  console.log(chalk26.bold(`Relations: ${stats.relations.total}`));
62984
63036
  for (const r of relationRows) {
62985
- console.log(` ${chalk26.cyan(r.relation_type)}: ${r.count}`);
63037
+ console.log(` ${chalk26.cyan(`${r.relation_type}:`)} ${r.count}`);
62986
63038
  }
62987
63039
  console.log();
62988
63040
  console.log(chalk26.bold(`Memory links: ${stats.memory_links}`));
@@ -64023,7 +64075,7 @@ function registerToolsCommand(program2) {
64023
64075
  console.log(chalk34.bold(`
64024
64076
  Common errors:`));
64025
64077
  for (const err of stats.common_errors) {
64026
- console.log(` ${chalk34.red(err.error_type)}: ${err.count} times`);
64078
+ console.log(` ${chalk34.red(`${err.error_type}:`)} ${err.count} times`);
64027
64079
  }
64028
64080
  }
64029
64081
  if (lessons.length === 0) {
@@ -65030,10 +65082,13 @@ function resolveStoreBackend() {
65030
65082
  const apiConfig = getApiConfig();
65031
65083
  const storageMode = getStorageMode();
65032
65084
  const sources = getApiModeEnvSources();
65085
+ const configured = getConfiguredApiEnv();
65033
65086
  const backend = apiMode ? "cloud-api" : storageMode === "cloud" ? "cloud-postgres" : "local-sqlite";
65034
65087
  let selectedBy = "default";
65035
65088
  if (apiMode) {
65036
65089
  selectedBy = `${sources.urlKey} + ${sources.keyKey} (presence)`;
65090
+ } else if (backend === "local-sqlite" && configured.dbPathKey) {
65091
+ selectedBy = configured.apiKeyPresent && configured.baseUrl ? `${configured.dbPathKey} (explicit local path, outranks the API selectors)` : `${configured.dbPathKey} (explicit local path)`;
65037
65092
  } else if (backend === "cloud-postgres") {
65038
65093
  const modeKey = [MEMENTOS_STORAGE_ENV.mode, MEMENTOS_STORAGE_FALLBACK_ENV.mode].find((key) => process.env[key]?.trim());
65039
65094
  selectedBy = modeKey ?? sources.databaseUrlKey ?? "storage config file";
@@ -65044,8 +65099,8 @@ function resolveStoreBackend() {
65044
65099
  api_mode: apiMode,
65045
65100
  storage_mode: storageMode,
65046
65101
  db_path: getDbPath(),
65047
- api_endpoint: apiConfig?.baseUrl ?? null,
65048
- api_key_present: Boolean(apiConfig?.apiKey),
65102
+ api_endpoint: apiConfig?.baseUrl ?? configured.baseUrl,
65103
+ api_key_present: Boolean(apiConfig?.apiKey) || configured.apiKeyPresent,
65049
65104
  selected_by: selectedBy
65050
65105
  };
65051
65106
  }
@@ -65099,6 +65154,9 @@ function installStorageSubcommands(storage, program2) {
65099
65154
  console.log(`Storage mode: ${report.storage_mode}`);
65100
65155
  if (report.backend === "local-sqlite") {
65101
65156
  console.log(`Database: ${report.db_path}`);
65157
+ if (report.api_key_present && report.api_endpoint) {
65158
+ console.log(`API endpoint (configured, OUTRANKED): ${report.api_endpoint}`);
65159
+ }
65102
65160
  } else {
65103
65161
  console.log(`API endpoint: ${report.api_endpoint ?? "(none)"}`);
65104
65162
  console.log(`API key: ${report.api_key_present ? "configured" : "not configured"}`);
@@ -66947,6 +67005,14 @@ function getPackageVersion2() {
66947
67005
  var program2 = new Command;
66948
67006
  program2.name("mementos").description("Universal memory system for AI agents").version(getPackageVersion2());
66949
67007
  applyGlobalOptions(program2);
67008
+ function applyExitCodeContract(command) {
67009
+ command.exitOverride((err) => {
67010
+ process.exit(resolveExitCode(err));
67011
+ });
67012
+ for (const subcommand of command.commands) {
67013
+ applyExitCodeContract(subcommand);
67014
+ }
67015
+ }
66950
67016
  var startupWarningShown = false;
66951
67017
  program2.hook("preAction", (_thisCommand, actionCommand) => {
66952
67018
  if (skipsStartupDbAccess(actionCommand))
@@ -66962,4 +67028,5 @@ program2.hook("preAction", (_thisCommand, actionCommand) => {
66962
67028
  } catch {}
66963
67029
  });
66964
67030
  registerAllCommands(program2);
67031
+ applyExitCodeContract(program2);
66965
67032
  program2.parse(process.argv);
@@ -15,6 +15,23 @@ export declare function getApiModeEnvSources(): {
15
15
  keyKey: string | null;
16
16
  databaseUrlKey: string | null;
17
17
  };
18
+ /**
19
+ * What the environment CONFIGURES, regardless of which store finally wins.
20
+ *
21
+ * Distinct from {@link getApiConfig}, which answers "may I build a cloud
22
+ * request?" and returns null once an explicit DB_PATH outranks the credentials
23
+ * (precedence 1). An operator report needs both halves separately: conflating
24
+ * them prints "no API key configured" at someone whose key is configured and
25
+ * merely outranked, which points them at a credential problem they do not have.
26
+ *
27
+ * Never returns a credential value — the endpoint is not secret, the key is
28
+ * reported only as a boolean.
29
+ */
30
+ export declare function getConfiguredApiEnv(): {
31
+ baseUrl: string | null;
32
+ apiKeyPresent: boolean;
33
+ dbPathKey: string | null;
34
+ };
18
35
  /**
19
36
  * Escape hatch for a test that genuinely must reach a non-loopback endpoint.
20
37
  * Opt-in by design: the default has to be safe, because the failure it prevents
@@ -38,13 +55,26 @@ export declare class MementosStoreConfigError extends Error {
38
55
  * dataset with no error and no flag. From the caller's side that is
39
56
  * indistinguishable from a store that is working.
40
57
  *
58
+ * THIS FUNCTION ONLY ASSERTS. It returns `void`, so it cannot select anything —
59
+ * the precedence table below describes the RESOLVER, which is `getApiConfig()`
60
+ * immediately after it, and each early `return` here only suppresses the
61
+ * ambiguity error for a case that is already unambiguous. Steps 1 and 2 are
62
+ * therefore implemented in `getApiConfig()` and `isApiMode()` respectively, and
63
+ * the table is documented here because this is where the error text lives.
64
+ *
65
+ * Keeping the table honest about that is not cosmetic. Until 2026-08-03 step 1
66
+ * was implemented NOWHERE: the `return` below reads as a selection, and an
67
+ * operator who read it concluded that a scratch DB_PATH made them safe while a
68
+ * fully-configured environment still routed every read and write to the shared
69
+ * production store.
70
+ *
41
71
  * Precedence, highest first:
42
72
  * 1. An explicit SQLite path (`HASNA_MEMENTOS_DB_PATH` / `MEMENTOS_DB_PATH`) is
43
73
  * the narrowest, most specific signal and selects LOCAL, so local dev,
44
74
  * tooling and import/export keep working when a stray credential is exported
45
- * globally.
46
- * 2. A client DSN present disables API mode. Unchanged here, and tracked
47
- * separately — a DSN on a client is forbidden and handled in database.ts.
75
+ * globally. ENFORCED in `getApiConfig()`, which returns null for it.
76
+ * 2. A client DSN present disables API mode. ENFORCED in `isApiMode()`. A DSN
77
+ * on a client is forbidden and is tracked separately in database.ts.
48
78
  * 3. Both API URL and API key present -> API mode.
49
79
  * 4. Exactly ONE of them present -> ERROR naming the missing variable.
50
80
  * 5. Neither present -> LOCAL. The documented single-operator default.
@@ -1 +1 @@
1
- {"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AA0CA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,qBAAqB,mEAAoE,CAAC;AAsBvG;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAMtH;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uCAAuC,CAAC;AAyDlF,6EAA6E;AAC7E,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,2BAA2B;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAuBhD;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAM/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AA0GD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAiB7B;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAe/D"}
1
+ {"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AA0CA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,qBAAqB,mEAAoE,CAAC;AAsBvG;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAMtH;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAOA;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uCAAuC,CAAC;AAyDlF,6EAA6E;AAC7E,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,2BAA2B;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CA0BhD;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAyB/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AA0GD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAiB7B;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAe/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,IAAI,QAAQ,EAK1B,MAAM,eAAe,CAAC;AAkDvB,wBAAgB,SAAS,IAAI,MAAM,CAoBlC;AAoDD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CA4DrD;AAgDD,wBAAgB,aAAa,IAAI,IAAI,CASpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAuBf"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,IAAI,QAAQ,EAK1B,MAAM,eAAe,CAAC;AAkDvB,wBAAgB,SAAS,IAAI,MAAM,CAwBlC;AAoDD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAoGrD;AAgDD,wBAAgB,aAAa,IAAI,IAAI,CASpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAuBf"}
@@ -16,9 +16,18 @@ export interface StoreBackendReport {
16
16
  storage_mode: string;
17
17
  /** The SQLite path that WOULD be used. Meaningful only for `local-sqlite`. */
18
18
  db_path: string;
19
- /** Cloud endpoint origin + prefix. Not a secret; the key is never included. */
19
+ /**
20
+ * Cloud endpoint origin + prefix as CONFIGURED, not necessarily as used: it
21
+ * is still reported when an explicit DB_PATH has outranked it, because the
22
+ * operator needs to see what was set as well as what won. Read `backend` /
23
+ * `api_mode` for what is actually in force, and `selected_by` for why.
24
+ * Not a secret; the key is never included.
25
+ */
20
26
  api_endpoint: string | null;
21
- /** Whether an API key is configured. The value is never read or reported. */
27
+ /**
28
+ * Whether an API key is CONFIGURED in the environment — again independent of
29
+ * whether it won. The value is never read or reported.
30
+ */
22
31
  api_key_present: boolean;
23
32
  /** Env key NAMES that produced this backend, or `"default"`. Names only. */
24
33
  selected_by: string;
@@ -1 +1 @@
1
- {"version":3,"file":"store-backend.d.ts","sourceRoot":"","sources":["../../src/db/store-backend.ts"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAkCxD"}
1
+ {"version":3,"file":"store-backend.d.ts","sourceRoot":"","sources":["../../src/db/store-backend.ts"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAoDxD"}
package/dist/index.js CHANGED
@@ -1031,6 +1031,8 @@ function assertUnambiguousStoreEnv() {
1031
1031
  }
1032
1032
  function getApiConfig() {
1033
1033
  assertUnambiguousStoreEnv();
1034
+ if (firstEnvKey(DB_PATH_ENV_KEYS))
1035
+ return null;
1034
1036
  const rawBase = firstEnv(API_URL_ENV_KEYS);
1035
1037
  const apiKey = firstEnv(API_KEY_ENV_KEYS);
1036
1038
  if (!rawBase || !apiKey)
@@ -2155,9 +2157,10 @@ function migrateGlobalDir() {
2155
2157
  }
2156
2158
  }
2157
2159
  function getDbPath() {
2158
- const envPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
2159
- if (envPath) {
2160
- return envPath;
2160
+ for (const key of DB_PATH_ENV_KEYS) {
2161
+ const envPath = process.env[key]?.trim();
2162
+ if (envPath)
2163
+ return envPath;
2161
2164
  }
2162
2165
  const cwd = process.cwd();
2163
2166
  const nearest = findNearestMementosDb(cwd);
@@ -2201,6 +2204,12 @@ function getDatabase(dbPath) {
2201
2204
  if (getStorageMode() === "cloud") {
2202
2205
  return getCloudDatabase();
2203
2206
  }
2207
+ if (process.env["NODE_ENV"] === "test") {
2208
+ const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
2209
+ if (!hasExplicitDbPath) {
2210
+ throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
2211
+ }
2212
+ }
2204
2213
  }
2205
2214
  const path = dbPath || getDbPath();
2206
2215
  if (_db) {
package/dist/mcp/index.js CHANGED
@@ -1094,6 +1094,8 @@ function assertUnambiguousStoreEnv() {
1094
1094
  }
1095
1095
  function getApiConfig() {
1096
1096
  assertUnambiguousStoreEnv();
1097
+ if (firstEnvKey(DB_PATH_ENV_KEYS))
1098
+ return null;
1097
1099
  const rawBase = firstEnv(API_URL_ENV_KEYS);
1098
1100
  const apiKey = firstEnv(API_KEY_ENV_KEYS);
1099
1101
  if (!rawBase || !apiKey)
@@ -2218,9 +2220,10 @@ function migrateGlobalDir() {
2218
2220
  }
2219
2221
  }
2220
2222
  function getDbPath() {
2221
- const envPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
2222
- if (envPath) {
2223
- return envPath;
2223
+ for (const key of DB_PATH_ENV_KEYS) {
2224
+ const envPath = process.env[key]?.trim();
2225
+ if (envPath)
2226
+ return envPath;
2224
2227
  }
2225
2228
  const cwd = process.cwd();
2226
2229
  const nearest = findNearestMementosDb(cwd);
@@ -2264,6 +2267,12 @@ function getDatabase(dbPath) {
2264
2267
  if (getStorageMode() === "cloud") {
2265
2268
  return getCloudDatabase();
2266
2269
  }
2270
+ if (process.env["NODE_ENV"] === "test") {
2271
+ const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
2272
+ if (!hasExplicitDbPath) {
2273
+ throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
2274
+ }
2275
+ }
2267
2276
  }
2268
2277
  const path = dbPath || getDbPath();
2269
2278
  if (_db) {
@@ -655,6 +655,8 @@ function assertUnambiguousStoreEnv() {
655
655
  }
656
656
  function getApiConfig() {
657
657
  assertUnambiguousStoreEnv();
658
+ if (firstEnvKey(DB_PATH_ENV_KEYS))
659
+ return null;
658
660
  const rawBase = firstEnv(API_URL_ENV_KEYS);
659
661
  const apiKey = firstEnv(API_KEY_ENV_KEYS);
660
662
  if (!rawBase || !apiKey)
@@ -1779,9 +1781,10 @@ function migrateGlobalDir() {
1779
1781
  }
1780
1782
  }
1781
1783
  function getDbPath2() {
1782
- const envPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
1783
- if (envPath) {
1784
- return envPath;
1784
+ for (const key of DB_PATH_ENV_KEYS) {
1785
+ const envPath = process.env[key]?.trim();
1786
+ if (envPath)
1787
+ return envPath;
1785
1788
  }
1786
1789
  const cwd = process.cwd();
1787
1790
  const nearest = findNearestMementosDb(cwd);
@@ -1825,6 +1828,12 @@ function getDatabase(dbPath) {
1825
1828
  if (getStorageMode() === "cloud") {
1826
1829
  return getCloudDatabase();
1827
1830
  }
1831
+ if (process.env["NODE_ENV"] === "test") {
1832
+ const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
1833
+ if (!hasExplicitDbPath) {
1834
+ throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath2()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
1835
+ }
1836
+ }
1828
1837
  }
1829
1838
  const path = dbPath || getDbPath2();
1830
1839
  if (_db) {
@@ -31,8 +31,6 @@ export interface IsolatedStoreEnvOptions {
31
31
  */
32
32
  export declare function isolatedStoreEnv(dbPath: string, options?: IsolatedStoreEnvOptions): Record<string, string>;
33
33
  export interface StubApiEnvOptions {
34
- /** Pin the (non-authoritative) local SQLite path too, for belt and braces. */
35
- dbPath?: string;
36
34
  /** Stub bearer token. Never a real credential — the stub does not check it. */
37
35
  apiKey?: string;
38
36
  }
@@ -48,6 +46,28 @@ export interface StubApiEnvOptions {
48
46
  * @param baseUrl the stub's origin, e.g. `http://127.0.0.1:<port>/<mode>`
49
47
  */
50
48
  export declare function stubApiEnv(baseUrl: string, options?: StubApiEnvOptions): Record<string, string>;
49
+ /**
50
+ * Build a child env that DOES resolve to the cloud API — the negative case the
51
+ * isolation guards are proved against.
52
+ *
53
+ * This exists because of the 2026-08-03 precedence-1 fix. Before it, a cloud
54
+ * case could be built by taking any env and adding an API url+key, and several
55
+ * tests did exactly that on top of {@link isolatedStoreEnv}. An explicit
56
+ * `MEMENTOS_DB_PATH` now DEFEATS the API selectors (see getApiConfig in
57
+ * src/db/api-mode.ts), so that recipe silently produces a LOCAL backend — which
58
+ * would leave the isolation guards' positive controls unable to observe the bad
59
+ * state they exist to detect, and `assertLocalStoreBackend`'s own self-test
60
+ * unable to fail. Both would still be green.
61
+ *
62
+ * So the DB_PATH keys are removed here explicitly, and that removal is the
63
+ * load-bearing line rather than an afterthought: it is the only way to reach the
64
+ * cloud backend now. They are NOT in STORE_SELECTOR_ENV_KEYS because that set
65
+ * means "moves the store OFF local SQLite", and DB_PATH does the opposite.
66
+ *
67
+ * @param baseUrl point this at a closed loopback port, never a real endpoint
68
+ * @param apiKey never a real credential
69
+ */
70
+ export declare function cloudSelectingEnv(baseUrl: string, apiKey?: string): Record<string, string>;
51
71
  /** LLM provider keys most harnesses blank so no test can make a billed call. */
52
72
  export declare const LLM_PROVIDER_ENV_KEYS: readonly ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "CEREBRAS_API_KEY", "XAI_API_KEY"];
53
73
  /** `{ ANTHROPIC_API_KEY: "", ... }` — pass as `extra` to blank LLM providers. */
@@ -1 +1 @@
1
- {"version":3,"file":"store-isolation.d.ts","sourceRoot":"","sources":["../../src/test-support/store-isolation.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAWpD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxB;AASD,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQnG;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,qFAKxB,CAAC;AAEX,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAsD7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,SAA0B,GAAG,IAAI,CAQ9F"}
1
+ {"version":3,"file":"store-isolation.d.ts","sourceRoot":"","sources":["../../src/test-support/store-isolation.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAWpD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxB;AASD,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;CAUjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQnG;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,SAAmB,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,qFAKxB,CAAC;AAEX,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAsD7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,SAA0B,GAAG,IAAI,CAQ9F"}
@@ -0,0 +1,3 @@
1
+ /** Remove ANSI/VT100 SGR escape sequences from CLI output before asserting on content. */
2
+ export declare function stripAnsi(input: string): string;
3
+ //# sourceMappingURL=strip-ansi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-ansi.d.ts","sourceRoot":"","sources":["../../src/test-support/strip-ansi.ts"],"names":[],"mappings":"AAyBA,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.14.72",
3
+ "version": "0.14.74",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",