@frontmcp/plugin-remember 1.8.7 → 1.9.1-rc.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/esm/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
2
+ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
+ // __frontmcpCreateRequire
1
4
  var __defProp = Object.defineProperty;
2
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
6
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -405,6 +408,9 @@ var STORAGE_LAYOUT_VERSION = "v2";
405
408
  function encodeKeyPart(value) {
406
409
  return encodeURIComponent(value);
407
410
  }
411
+ function remainingSeconds(expiresAt, now) {
412
+ return Math.max(1, Math.ceil((expiresAt - now) / 1e3));
413
+ }
408
414
  var RememberAccessor = class {
409
415
  store;
410
416
  ctx;
@@ -454,16 +460,8 @@ var RememberAccessor = class {
454
460
  */
455
461
  async get(key, options = {}) {
456
462
  const scope = options.scope ?? "session";
457
- const storageKey = this.buildStorageKey(key, scope);
458
- const raw = await this.store.getValue(storageKey);
459
- if (!raw) return options.defaultValue;
460
- const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
461
- if (!entry) return options.defaultValue;
462
- if (entry.expiresAt && Date.now() > entry.expiresAt) {
463
- await this.forget(key, { scope });
464
- return options.defaultValue;
465
- }
466
- return entry.value;
463
+ const entry = await this.readEntry(this.buildStorageKey(key, scope), scope);
464
+ return entry ? entry.value : options.defaultValue;
467
465
  }
468
466
  /**
469
467
  * Get the full entry including metadata.
@@ -474,16 +472,7 @@ var RememberAccessor = class {
474
472
  */
475
473
  async getEntry(key, options = {}) {
476
474
  const scope = options.scope ?? "session";
477
- const storageKey = this.buildStorageKey(key, scope);
478
- const raw = await this.store.getValue(storageKey);
479
- if (!raw) return void 0;
480
- const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
481
- if (!entry) return void 0;
482
- if (entry.expiresAt && Date.now() > entry.expiresAt) {
483
- await this.forget(key, { scope });
484
- return void 0;
485
- }
486
- return entry;
475
+ return this.readEntry(this.buildStorageKey(key, scope), scope);
487
476
  }
488
477
  /**
489
478
  * Forget (delete) a value from memory.
@@ -497,19 +486,18 @@ var RememberAccessor = class {
497
486
  await this.store.delete(storageKey);
498
487
  }
499
488
  /**
500
- * Check if a key is remembered (exists and not expired).
489
+ * Check if a key is remembered: true exactly when `get()` would return its value.
501
490
  *
502
491
  * @param key - The key to check
503
492
  * @param options - Options (scope)
504
- * @returns true if the key exists
493
+ * @returns true if the key exists and has not expired
505
494
  */
506
495
  async knows(key, options = {}) {
507
496
  const scope = options.scope ?? "session";
508
- const storageKey = this.buildStorageKey(key, scope);
509
- return this.store.exists(storageKey);
497
+ return await this.readEntry(this.buildStorageKey(key, scope), scope) !== void 0;
510
498
  }
511
499
  /**
512
- * List all remembered keys for a scope.
500
+ * List all remembered keys for a scope: the keys `get()` would return a value for.
513
501
  *
514
502
  * @param options - Options (scope, pattern)
515
503
  * @returns Array of keys (without the scope prefix)
@@ -517,13 +505,16 @@ var RememberAccessor = class {
517
505
  async list(options = {}) {
518
506
  const scope = options.scope ?? "session";
519
507
  const scopePrefix = this.buildScopePrefix(scope);
520
- const fullPattern = scopePrefix + (options.pattern ?? "*");
521
- const keys = await this.store.keys(fullPattern);
522
- return keys.map((k) => k.slice(scopePrefix.length));
508
+ const storageKeys = await this.store.keys(scopePrefix + (options.pattern ?? "*"));
509
+ const entries = await Promise.all(storageKeys.map((storageKey) => this.readEntry(storageKey, scope)));
510
+ return storageKeys.filter((_storageKey, index) => entries[index] !== void 0).map((storageKey) => storageKey.slice(scopePrefix.length));
523
511
  }
524
512
  /**
525
513
  * Update an existing entry's value while preserving metadata.
526
514
  *
515
+ * With a `ttl` the entry expires that many seconds from now; without one it keeps its current
516
+ * expiry, in the store as well, so the store does not keep it past that (rounded up to a second).
517
+ *
527
518
  * @param key - The key to update
528
519
  * @param value - The new value
529
520
  * @param options - Options (scope, ttl)
@@ -532,15 +523,18 @@ var RememberAccessor = class {
532
523
  const scope = options.scope ?? "session";
533
524
  const existing = await this.getEntry(key, { scope });
534
525
  if (!existing) return false;
526
+ const now = Date.now();
527
+ const expiresAt = options.ttl ? now + options.ttl * 1e3 : existing.expiresAt;
535
528
  const entry = {
536
529
  ...existing,
537
530
  value,
538
- updatedAt: Date.now(),
539
- expiresAt: options.ttl ? Date.now() + options.ttl * 1e3 : existing.expiresAt
531
+ updatedAt: now,
532
+ expiresAt
540
533
  };
541
534
  const storageKey = this.buildStorageKey(key, scope);
542
535
  const serialized = this.encryptionEnabled ? await encryptAndSerialize(entry, this.getKeySource(scope)) : JSON.stringify(entry);
543
- await this.store.setValue(storageKey, serialized, options.ttl);
536
+ const storageTtl = options.ttl ?? (expiresAt !== void 0 ? remainingSeconds(expiresAt, now) : void 0);
537
+ await this.store.setValue(storageKey, serialized, storageTtl);
544
538
  return true;
545
539
  }
546
540
  // ─────────────────────────────────────────────────────────────────────────────
@@ -658,6 +652,19 @@ var RememberAccessor = class {
658
652
  toolName: scope === "tool" ? this.toolName : void 0
659
653
  });
660
654
  }
655
+ /** The entry stored under `storageKey`, if readable and unexpired; an expired one is deleted. */
656
+ async readEntry(storageKey, scope) {
657
+ const stored = await this.store.getValue(storageKey);
658
+ if (!stored) return void 0;
659
+ const raw = typeof stored === "string" ? stored : JSON.stringify(stored);
660
+ const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
661
+ if (!entry) return void 0;
662
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
663
+ await this.store.delete(storageKey);
664
+ return void 0;
665
+ }
666
+ return entry;
667
+ }
661
668
  /**
662
669
  * Parse a JSON entry (when encryption is disabled).
663
670
  */
@@ -907,7 +914,7 @@ var RememberRedisProvider = class {
907
914
  }
908
915
  }
909
916
  const fullKey = this.keyPrefix + key;
910
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
917
+ const strValue = JSON.stringify(value);
911
918
  if (ttlSeconds !== void 0 && ttlSeconds > 0) {
912
919
  await this.client.set(fullKey, strValue, "EX", ttlSeconds);
913
920
  } else {
@@ -922,7 +929,7 @@ var RememberRedisProvider = class {
922
929
  */
923
930
  async setIfAbsent(key, value, ttlSeconds) {
924
931
  const fullKey = this.keyPrefix + key;
925
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
932
+ const strValue = JSON.stringify(value);
926
933
  const result = ttlSeconds !== void 0 && ttlSeconds > 0 ? await this.client.set(fullKey, strValue, "EX", ttlSeconds, "NX") : await this.client.set(fullKey, strValue, "NX");
927
934
  return result === "OK";
928
935
  }
@@ -999,8 +1006,11 @@ RememberRedisProvider = __decorateClass([
999
1006
 
1000
1007
  // plugins/plugin-remember/src/providers/remember-vercel-kv.provider.ts
1001
1008
  import { Provider as Provider4, ProviderScope as ProviderScope4 } from "@frontmcp/sdk";
1009
+ import { getEnv } from "@frontmcp/utils";
1002
1010
  var RememberVercelKvProvider = class {
1003
- kv;
1011
+ createClient;
1012
+ connection;
1013
+ client;
1004
1014
  keyPrefix;
1005
1015
  defaultTTL;
1006
1016
  constructor(options = {}) {
@@ -1012,17 +1022,23 @@ var RememberVercelKvProvider = class {
1012
1022
  `RememberVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. Received: url=${hasUrl ? "provided" : "missing"}, token=${hasToken ? "provided" : "missing"}`
1013
1023
  );
1014
1024
  }
1015
- if (options.url && options.token) {
1016
- this.kv = vercelKv.createClient({
1017
- url: options.url,
1018
- token: options.token
1019
- });
1020
- } else {
1021
- this.kv = vercelKv.kv;
1022
- }
1025
+ this.createClient = vercelKv.createClient;
1026
+ this.connection = { url: options.url, token: options.token };
1023
1027
  this.keyPrefix = options.keyPrefix ?? "remember:";
1024
1028
  this.defaultTTL = options.defaultTTL;
1025
1029
  }
1030
+ /** Built on first use, like the module's `kv` singleton, which would JSON-parse what it reads. */
1031
+ get kv() {
1032
+ if (!this.client) {
1033
+ const url = this.connection.url ?? getEnv("KV_REST_API_URL");
1034
+ const token = this.connection.token ?? getEnv("KV_REST_API_TOKEN");
1035
+ if (!url || !token) {
1036
+ throw new Error("RememberVercelKvProvider: pass url and token, or set KV_REST_API_URL and KV_REST_API_TOKEN.");
1037
+ }
1038
+ this.client = this.createClient({ url, token, automaticDeserialization: false });
1039
+ }
1040
+ return this.client;
1041
+ }
1026
1042
  prefixKey(key) {
1027
1043
  return `${this.keyPrefix}${key}`;
1028
1044
  }
@@ -1031,7 +1047,7 @@ var RememberVercelKvProvider = class {
1031
1047
  */
1032
1048
  async setValue(key, value, ttlSeconds) {
1033
1049
  const fullKey = this.prefixKey(key);
1034
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
1050
+ const strValue = JSON.stringify(value);
1035
1051
  const ttl = ttlSeconds ?? this.defaultTTL;
1036
1052
  if (ttl && ttl > 0) {
1037
1053
  await this.kv.set(fullKey, strValue, { ex: ttl });
@@ -1047,7 +1063,7 @@ var RememberVercelKvProvider = class {
1047
1063
  */
1048
1064
  async setIfAbsent(key, value, ttlSeconds) {
1049
1065
  const fullKey = this.prefixKey(key);
1050
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
1066
+ const strValue = JSON.stringify(value);
1051
1067
  const ttl = ttlSeconds ?? this.defaultTTL;
1052
1068
  const result = ttl && ttl > 0 ? await this.kv.set(fullKey, strValue, { nx: true, ex: ttl }) : await this.kv.set(fullKey, strValue, { nx: true });
1053
1069
  return result === "OK";
@@ -1058,15 +1074,12 @@ var RememberVercelKvProvider = class {
1058
1074
  async getValue(key, defaultValue) {
1059
1075
  const fullKey = this.prefixKey(key);
1060
1076
  const raw = await this.kv.get(fullKey);
1061
- if (raw === null || raw === void 0) return defaultValue;
1062
- if (typeof raw === "string") {
1063
- try {
1064
- return JSON.parse(raw);
1065
- } catch {
1066
- return raw;
1067
- }
1077
+ if (raw === null) return defaultValue;
1078
+ try {
1079
+ return JSON.parse(raw);
1080
+ } catch {
1081
+ return raw;
1068
1082
  }
1069
- return raw;
1070
1083
  }
1071
1084
  /**
1072
1085
  * Delete a key.
@@ -1100,7 +1113,7 @@ var RememberVercelKvProvider = class {
1100
1113
  for (const key of keys) {
1101
1114
  result.push(key.slice(this.keyPrefix.length));
1102
1115
  }
1103
- } while (cursor !== 0);
1116
+ } while (String(cursor) !== "0");
1104
1117
  } catch {
1105
1118
  try {
1106
1119
  const keys = await this.kv.keys(searchPattern);
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-remember",
3
- "version": "1.8.7",
3
+ "version": "1.9.1-rc.1",
4
4
  "description": "Remember plugin for FrontMCP - encrypted session memory with approval system for secure tool authorization",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -49,12 +49,12 @@
49
49
  }
50
50
  },
51
51
  "dependencies": {
52
- "@frontmcp/sdk": "1.8.7",
53
- "@frontmcp/utils": "1.8.7",
52
+ "@frontmcp/sdk": "1.9.1-rc.1",
53
+ "@frontmcp/utils": "1.9.1-rc.1",
54
54
  "ioredis": "^5.8.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@frontmcp/lazy-zod": "1.8.7",
57
+ "@frontmcp/lazy-zod": "1.9.1-rc.1",
58
58
  "@vercel/kv": "^2.0.0 || ^3.0.0"
59
59
  },
60
60
  "peerDependenciesMeta": {
package/index.js CHANGED
@@ -427,6 +427,9 @@ var STORAGE_LAYOUT_VERSION = "v2";
427
427
  function encodeKeyPart(value) {
428
428
  return encodeURIComponent(value);
429
429
  }
430
+ function remainingSeconds(expiresAt, now) {
431
+ return Math.max(1, Math.ceil((expiresAt - now) / 1e3));
432
+ }
430
433
  var RememberAccessor = class {
431
434
  store;
432
435
  ctx;
@@ -476,16 +479,8 @@ var RememberAccessor = class {
476
479
  */
477
480
  async get(key, options = {}) {
478
481
  const scope = options.scope ?? "session";
479
- const storageKey = this.buildStorageKey(key, scope);
480
- const raw = await this.store.getValue(storageKey);
481
- if (!raw) return options.defaultValue;
482
- const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
483
- if (!entry) return options.defaultValue;
484
- if (entry.expiresAt && Date.now() > entry.expiresAt) {
485
- await this.forget(key, { scope });
486
- return options.defaultValue;
487
- }
488
- return entry.value;
482
+ const entry = await this.readEntry(this.buildStorageKey(key, scope), scope);
483
+ return entry ? entry.value : options.defaultValue;
489
484
  }
490
485
  /**
491
486
  * Get the full entry including metadata.
@@ -496,16 +491,7 @@ var RememberAccessor = class {
496
491
  */
497
492
  async getEntry(key, options = {}) {
498
493
  const scope = options.scope ?? "session";
499
- const storageKey = this.buildStorageKey(key, scope);
500
- const raw = await this.store.getValue(storageKey);
501
- if (!raw) return void 0;
502
- const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
503
- if (!entry) return void 0;
504
- if (entry.expiresAt && Date.now() > entry.expiresAt) {
505
- await this.forget(key, { scope });
506
- return void 0;
507
- }
508
- return entry;
494
+ return this.readEntry(this.buildStorageKey(key, scope), scope);
509
495
  }
510
496
  /**
511
497
  * Forget (delete) a value from memory.
@@ -519,19 +505,18 @@ var RememberAccessor = class {
519
505
  await this.store.delete(storageKey);
520
506
  }
521
507
  /**
522
- * Check if a key is remembered (exists and not expired).
508
+ * Check if a key is remembered: true exactly when `get()` would return its value.
523
509
  *
524
510
  * @param key - The key to check
525
511
  * @param options - Options (scope)
526
- * @returns true if the key exists
512
+ * @returns true if the key exists and has not expired
527
513
  */
528
514
  async knows(key, options = {}) {
529
515
  const scope = options.scope ?? "session";
530
- const storageKey = this.buildStorageKey(key, scope);
531
- return this.store.exists(storageKey);
516
+ return await this.readEntry(this.buildStorageKey(key, scope), scope) !== void 0;
532
517
  }
533
518
  /**
534
- * List all remembered keys for a scope.
519
+ * List all remembered keys for a scope: the keys `get()` would return a value for.
535
520
  *
536
521
  * @param options - Options (scope, pattern)
537
522
  * @returns Array of keys (without the scope prefix)
@@ -539,13 +524,16 @@ var RememberAccessor = class {
539
524
  async list(options = {}) {
540
525
  const scope = options.scope ?? "session";
541
526
  const scopePrefix = this.buildScopePrefix(scope);
542
- const fullPattern = scopePrefix + (options.pattern ?? "*");
543
- const keys = await this.store.keys(fullPattern);
544
- return keys.map((k) => k.slice(scopePrefix.length));
527
+ const storageKeys = await this.store.keys(scopePrefix + (options.pattern ?? "*"));
528
+ const entries = await Promise.all(storageKeys.map((storageKey) => this.readEntry(storageKey, scope)));
529
+ return storageKeys.filter((_storageKey, index) => entries[index] !== void 0).map((storageKey) => storageKey.slice(scopePrefix.length));
545
530
  }
546
531
  /**
547
532
  * Update an existing entry's value while preserving metadata.
548
533
  *
534
+ * With a `ttl` the entry expires that many seconds from now; without one it keeps its current
535
+ * expiry, in the store as well, so the store does not keep it past that (rounded up to a second).
536
+ *
549
537
  * @param key - The key to update
550
538
  * @param value - The new value
551
539
  * @param options - Options (scope, ttl)
@@ -554,15 +542,18 @@ var RememberAccessor = class {
554
542
  const scope = options.scope ?? "session";
555
543
  const existing = await this.getEntry(key, { scope });
556
544
  if (!existing) return false;
545
+ const now = Date.now();
546
+ const expiresAt = options.ttl ? now + options.ttl * 1e3 : existing.expiresAt;
557
547
  const entry = {
558
548
  ...existing,
559
549
  value,
560
- updatedAt: Date.now(),
561
- expiresAt: options.ttl ? Date.now() + options.ttl * 1e3 : existing.expiresAt
550
+ updatedAt: now,
551
+ expiresAt
562
552
  };
563
553
  const storageKey = this.buildStorageKey(key, scope);
564
554
  const serialized = this.encryptionEnabled ? await encryptAndSerialize(entry, this.getKeySource(scope)) : JSON.stringify(entry);
565
- await this.store.setValue(storageKey, serialized, options.ttl);
555
+ const storageTtl = options.ttl ?? (expiresAt !== void 0 ? remainingSeconds(expiresAt, now) : void 0);
556
+ await this.store.setValue(storageKey, serialized, storageTtl);
566
557
  return true;
567
558
  }
568
559
  // ─────────────────────────────────────────────────────────────────────────────
@@ -680,6 +671,19 @@ var RememberAccessor = class {
680
671
  toolName: scope === "tool" ? this.toolName : void 0
681
672
  });
682
673
  }
674
+ /** The entry stored under `storageKey`, if readable and unexpired; an expired one is deleted. */
675
+ async readEntry(storageKey, scope) {
676
+ const stored = await this.store.getValue(storageKey);
677
+ if (!stored) return void 0;
678
+ const raw = typeof stored === "string" ? stored : JSON.stringify(stored);
679
+ const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
680
+ if (!entry) return void 0;
681
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
682
+ await this.store.delete(storageKey);
683
+ return void 0;
684
+ }
685
+ return entry;
686
+ }
683
687
  /**
684
688
  * Parse a JSON entry (when encryption is disabled).
685
689
  */
@@ -929,7 +933,7 @@ var RememberRedisProvider = class {
929
933
  }
930
934
  }
931
935
  const fullKey = this.keyPrefix + key;
932
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
936
+ const strValue = JSON.stringify(value);
933
937
  if (ttlSeconds !== void 0 && ttlSeconds > 0) {
934
938
  await this.client.set(fullKey, strValue, "EX", ttlSeconds);
935
939
  } else {
@@ -944,7 +948,7 @@ var RememberRedisProvider = class {
944
948
  */
945
949
  async setIfAbsent(key, value, ttlSeconds) {
946
950
  const fullKey = this.keyPrefix + key;
947
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
951
+ const strValue = JSON.stringify(value);
948
952
  const result = ttlSeconds !== void 0 && ttlSeconds > 0 ? await this.client.set(fullKey, strValue, "EX", ttlSeconds, "NX") : await this.client.set(fullKey, strValue, "NX");
949
953
  return result === "OK";
950
954
  }
@@ -1021,8 +1025,11 @@ RememberRedisProvider = __decorateClass([
1021
1025
 
1022
1026
  // plugins/plugin-remember/src/providers/remember-vercel-kv.provider.ts
1023
1027
  var import_sdk5 = require("@frontmcp/sdk");
1028
+ var import_utils3 = require("@frontmcp/utils");
1024
1029
  var RememberVercelKvProvider = class {
1025
- kv;
1030
+ createClient;
1031
+ connection;
1032
+ client;
1026
1033
  keyPrefix;
1027
1034
  defaultTTL;
1028
1035
  constructor(options = {}) {
@@ -1034,17 +1041,23 @@ var RememberVercelKvProvider = class {
1034
1041
  `RememberVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. Received: url=${hasUrl ? "provided" : "missing"}, token=${hasToken ? "provided" : "missing"}`
1035
1042
  );
1036
1043
  }
1037
- if (options.url && options.token) {
1038
- this.kv = vercelKv.createClient({
1039
- url: options.url,
1040
- token: options.token
1041
- });
1042
- } else {
1043
- this.kv = vercelKv.kv;
1044
- }
1044
+ this.createClient = vercelKv.createClient;
1045
+ this.connection = { url: options.url, token: options.token };
1045
1046
  this.keyPrefix = options.keyPrefix ?? "remember:";
1046
1047
  this.defaultTTL = options.defaultTTL;
1047
1048
  }
1049
+ /** Built on first use, like the module's `kv` singleton, which would JSON-parse what it reads. */
1050
+ get kv() {
1051
+ if (!this.client) {
1052
+ const url = this.connection.url ?? (0, import_utils3.getEnv)("KV_REST_API_URL");
1053
+ const token = this.connection.token ?? (0, import_utils3.getEnv)("KV_REST_API_TOKEN");
1054
+ if (!url || !token) {
1055
+ throw new Error("RememberVercelKvProvider: pass url and token, or set KV_REST_API_URL and KV_REST_API_TOKEN.");
1056
+ }
1057
+ this.client = this.createClient({ url, token, automaticDeserialization: false });
1058
+ }
1059
+ return this.client;
1060
+ }
1048
1061
  prefixKey(key) {
1049
1062
  return `${this.keyPrefix}${key}`;
1050
1063
  }
@@ -1053,7 +1066,7 @@ var RememberVercelKvProvider = class {
1053
1066
  */
1054
1067
  async setValue(key, value, ttlSeconds) {
1055
1068
  const fullKey = this.prefixKey(key);
1056
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
1069
+ const strValue = JSON.stringify(value);
1057
1070
  const ttl = ttlSeconds ?? this.defaultTTL;
1058
1071
  if (ttl && ttl > 0) {
1059
1072
  await this.kv.set(fullKey, strValue, { ex: ttl });
@@ -1069,7 +1082,7 @@ var RememberVercelKvProvider = class {
1069
1082
  */
1070
1083
  async setIfAbsent(key, value, ttlSeconds) {
1071
1084
  const fullKey = this.prefixKey(key);
1072
- const strValue = typeof value === "string" ? value : JSON.stringify(value);
1085
+ const strValue = JSON.stringify(value);
1073
1086
  const ttl = ttlSeconds ?? this.defaultTTL;
1074
1087
  const result = ttl && ttl > 0 ? await this.kv.set(fullKey, strValue, { nx: true, ex: ttl }) : await this.kv.set(fullKey, strValue, { nx: true });
1075
1088
  return result === "OK";
@@ -1080,15 +1093,12 @@ var RememberVercelKvProvider = class {
1080
1093
  async getValue(key, defaultValue) {
1081
1094
  const fullKey = this.prefixKey(key);
1082
1095
  const raw = await this.kv.get(fullKey);
1083
- if (raw === null || raw === void 0) return defaultValue;
1084
- if (typeof raw === "string") {
1085
- try {
1086
- return JSON.parse(raw);
1087
- } catch {
1088
- return raw;
1089
- }
1096
+ if (raw === null) return defaultValue;
1097
+ try {
1098
+ return JSON.parse(raw);
1099
+ } catch {
1100
+ return raw;
1090
1101
  }
1091
- return raw;
1092
1102
  }
1093
1103
  /**
1094
1104
  * Delete a key.
@@ -1122,7 +1132,7 @@ var RememberVercelKvProvider = class {
1122
1132
  for (const key of keys) {
1123
1133
  result.push(key.slice(this.keyPrefix.length));
1124
1134
  }
1125
- } while (cursor !== 0);
1135
+ } while (String(cursor) !== "0");
1126
1136
  } catch {
1127
1137
  try {
1128
1138
  const keys = await this.kv.keys(searchPattern);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-remember",
3
- "version": "1.8.7",
3
+ "version": "1.9.1-rc.1",
4
4
  "description": "Remember plugin for FrontMCP - encrypted session memory with approval system for secure tool authorization",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -49,12 +49,12 @@
49
49
  }
50
50
  },
51
51
  "dependencies": {
52
- "@frontmcp/sdk": "1.8.7",
53
- "@frontmcp/utils": "1.8.7",
52
+ "@frontmcp/sdk": "1.9.1-rc.1",
53
+ "@frontmcp/utils": "1.9.1-rc.1",
54
54
  "ioredis": "^5.8.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@frontmcp/lazy-zod": "1.8.7",
57
+ "@frontmcp/lazy-zod": "1.9.1-rc.1",
58
58
  "@vercel/kv": "^2.0.0 || ^3.0.0"
59
59
  },
60
60
  "peerDependenciesMeta": {
@@ -66,15 +66,15 @@ export declare class RememberAccessor {
66
66
  */
67
67
  forget(key: string, options?: RememberForgetOptions): Promise<void>;
68
68
  /**
69
- * Check if a key is remembered (exists and not expired).
69
+ * Check if a key is remembered: true exactly when `get()` would return its value.
70
70
  *
71
71
  * @param key - The key to check
72
72
  * @param options - Options (scope)
73
- * @returns true if the key exists
73
+ * @returns true if the key exists and has not expired
74
74
  */
75
75
  knows(key: string, options?: RememberKnowsOptions): Promise<boolean>;
76
76
  /**
77
- * List all remembered keys for a scope.
77
+ * List all remembered keys for a scope: the keys `get()` would return a value for.
78
78
  *
79
79
  * @param options - Options (scope, pattern)
80
80
  * @returns Array of keys (without the scope prefix)
@@ -83,6 +83,9 @@ export declare class RememberAccessor {
83
83
  /**
84
84
  * Update an existing entry's value while preserving metadata.
85
85
  *
86
+ * With a `ttl` the entry expires that many seconds from now; without one it keeps its current
87
+ * expiry, in the store as well, so the store does not keep it past that (rounded up to a second).
88
+ *
86
89
  * @param key - The key to update
87
90
  * @param value - The new value
88
91
  * @param options - Options (scope, ttl)
@@ -149,6 +152,8 @@ export declare class RememberAccessor {
149
152
  * Get the encryption key source for a scope.
150
153
  */
151
154
  private getKeySource;
155
+ /** The entry stored under `storageKey`, if readable and unexpired; an expired one is deleted. */
156
+ private readEntry;
152
157
  /**
153
158
  * Parse a JSON entry (when encryption is disabled).
154
159
  */
@@ -1 +1 @@
1
- {"version":3,"file":"remember-accessor.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-accessor.provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remember-accessor.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-accessor.provider.ts"],"names":[],"mappings":"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"}
@@ -17,10 +17,14 @@ export interface RememberVercelKvProviderOptions {
17
17
  * Provides serverless-compatible, edge-ready storage.
18
18
  */
19
19
  export default class RememberVercelKvProvider implements RememberStoreInterface {
20
- private kv;
20
+ private readonly createClient;
21
+ private readonly connection;
22
+ private client?;
21
23
  private readonly keyPrefix;
22
24
  private readonly defaultTTL?;
23
25
  constructor(options?: RememberVercelKvProviderOptions);
26
+ /** Built on first use, like the module's `kv` singleton, which would JSON-parse what it reads. */
27
+ private get kv();
24
28
  private prefixKey;
25
29
  /**
26
30
  * Store a value with optional TTL.
@@ -1 +1 @@
1
- {"version":3,"file":"remember-vercel-kv.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-vercel-kv.provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAezE;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AAMH,MAAM,CAAC,OAAO,OAAO,wBAAyB,YAAW,sBAAsB;IAC7E,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;gBAEzB,OAAO,GAAE,+BAAoC;IA6BzD,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/E;;;;;OAKG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAarF;;OAEG;IACG,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAkBlF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK3C;;;OAGG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmC/C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
1
+ {"version":3,"file":"remember-vercel-kv.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-vercel-kv.provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAmBzE;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AAMH,MAAM,CAAC,OAAO,OAAO,wBAAyB,YAAW,sBAAsB;IAC7E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiC;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,MAAM,CAAC,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;gBAEzB,OAAO,GAAE,+BAAoC;IAoBzD,kGAAkG;IAClG,OAAO,KAAK,EAAE,GAUb;IAED,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/E;;;;;OAKG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAarF;;OAEG;IACG,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAalF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK3C;;;OAGG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmC/C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}