@frontmcp/plugin-remember 0.0.1 → 0.7.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.
Files changed (42) hide show
  1. package/esm/index.mjs +1289 -0
  2. package/esm/package.json +65 -0
  3. package/index.d.ts +53 -0
  4. package/index.d.ts.map +1 -0
  5. package/index.js +1316 -0
  6. package/package.json +3 -3
  7. package/providers/index.d.ts +7 -0
  8. package/providers/index.d.ts.map +1 -0
  9. package/providers/remember-accessor.provider.d.ts +124 -0
  10. package/providers/remember-accessor.provider.d.ts.map +1 -0
  11. package/providers/remember-memory.provider.d.ts +53 -0
  12. package/providers/remember-memory.provider.d.ts.map +1 -0
  13. package/providers/remember-redis.provider.d.ts +62 -0
  14. package/providers/remember-redis.provider.d.ts.map +1 -0
  15. package/providers/remember-storage.provider.d.ts +126 -0
  16. package/providers/remember-storage.provider.d.ts.map +1 -0
  17. package/providers/remember-store.interface.d.ts +42 -0
  18. package/providers/remember-store.interface.d.ts.map +1 -0
  19. package/providers/remember-vercel-kv.provider.d.ts +52 -0
  20. package/providers/remember-vercel-kv.provider.d.ts.map +1 -0
  21. package/remember.context-extension.d.ts +65 -0
  22. package/remember.context-extension.d.ts.map +1 -0
  23. package/remember.crypto.d.ts +81 -0
  24. package/remember.crypto.d.ts.map +1 -0
  25. package/remember.plugin.d.ts +41 -0
  26. package/remember.plugin.d.ts.map +1 -0
  27. package/remember.secret-persistence.d.ts +77 -0
  28. package/remember.secret-persistence.d.ts.map +1 -0
  29. package/remember.symbols.d.ts +28 -0
  30. package/remember.symbols.d.ts.map +1 -0
  31. package/remember.types.d.ts +206 -0
  32. package/remember.types.d.ts.map +1 -0
  33. package/tools/forget.tool.d.ts +32 -0
  34. package/tools/forget.tool.d.ts.map +1 -0
  35. package/tools/index.d.ts +9 -0
  36. package/tools/index.d.ts.map +1 -0
  37. package/tools/list-memories.tool.d.ts +33 -0
  38. package/tools/list-memories.tool.d.ts.map +1 -0
  39. package/tools/recall.tool.d.ts +38 -0
  40. package/tools/recall.tool.d.ts.map +1 -0
  41. package/tools/remember-this.tool.d.ts +42 -0
  42. package/tools/remember-this.tool.d.ts.map +1 -0
package/esm/index.mjs ADDED
@@ -0,0 +1,1289 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
10
+ var __decorateClass = (decorators, target, key, kind) => {
11
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
12
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
13
+ if (decorator = decorators[i])
14
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
15
+ if (kind && result) __defProp(target, key, result);
16
+ return result;
17
+ };
18
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
19
+
20
+ // plugins/plugin-remember/src/remember.plugin.ts
21
+ import {
22
+ DynamicPlugin,
23
+ Plugin,
24
+ ProviderScope as ProviderScope5,
25
+ FrontMcpConfig,
26
+ FRONTMCP_CONTEXT as FRONTMCP_CONTEXT2,
27
+ getGlobalStoreConfig,
28
+ isVercelKvProvider
29
+ } from "@frontmcp/sdk";
30
+
31
+ // plugins/plugin-remember/src/remember.symbols.ts
32
+ var RememberStoreToken = /* @__PURE__ */ Symbol(
33
+ "plugin:remember:store"
34
+ );
35
+ var RememberConfigToken = /* @__PURE__ */ Symbol(
36
+ "plugin:remember:config"
37
+ );
38
+ var RememberAccessorToken = /* @__PURE__ */ Symbol(
39
+ "plugin:remember:accessor"
40
+ );
41
+
42
+ // plugins/plugin-remember/src/providers/remember-memory.provider.ts
43
+ import { Provider, ProviderScope } from "@frontmcp/sdk";
44
+ var MAX_TIMEOUT_MS = 2 ** 31 - 1;
45
+ var RememberMemoryProvider = class {
46
+ memory = /* @__PURE__ */ new Map();
47
+ sweeper;
48
+ constructor(sweepIntervalSeconds = 60) {
49
+ this.sweeper = setInterval(() => this.sweep(), sweepIntervalSeconds * 1e3);
50
+ this.sweeper.unref?.();
51
+ }
52
+ /**
53
+ * Store a value with optional TTL.
54
+ * Always JSON-serializes the value for consistent storage/retrieval.
55
+ */
56
+ async setValue(key, value, ttlSeconds) {
57
+ const strValue = JSON.stringify(value);
58
+ const existing = this.memory.get(key);
59
+ if (existing?.timeout) clearTimeout(existing.timeout);
60
+ const entry = { value: strValue };
61
+ if (ttlSeconds && ttlSeconds > 0) {
62
+ const ttlMs = ttlSeconds * 1e3;
63
+ entry.expiresAt = Date.now() + ttlMs;
64
+ if (ttlMs <= MAX_TIMEOUT_MS) {
65
+ entry.timeout = setTimeout(() => {
66
+ const e = this.memory.get(key);
67
+ if (e && e.expiresAt && e.expiresAt <= Date.now()) {
68
+ this.memory.delete(key);
69
+ }
70
+ }, ttlMs);
71
+ entry.timeout.unref?.();
72
+ }
73
+ }
74
+ this.memory.set(key, entry);
75
+ }
76
+ /**
77
+ * Retrieve a value by key.
78
+ * JSON-parses the stored value to match the setValue serialization.
79
+ */
80
+ async getValue(key, defaultValue) {
81
+ const entry = this.memory.get(key);
82
+ if (!entry) return defaultValue;
83
+ if (this.isExpired(entry)) {
84
+ await this.delete(key);
85
+ return defaultValue;
86
+ }
87
+ try {
88
+ return JSON.parse(entry.value);
89
+ } catch {
90
+ return entry.value;
91
+ }
92
+ }
93
+ /**
94
+ * Delete a key.
95
+ */
96
+ async delete(key) {
97
+ const entry = this.memory.get(key);
98
+ if (entry?.timeout) clearTimeout(entry.timeout);
99
+ this.memory.delete(key);
100
+ }
101
+ /**
102
+ * Check if a key exists (and is not expired).
103
+ */
104
+ async exists(key) {
105
+ const entry = this.memory.get(key);
106
+ if (!entry) return false;
107
+ if (this.isExpired(entry)) {
108
+ await this.delete(key);
109
+ return false;
110
+ }
111
+ return true;
112
+ }
113
+ /**
114
+ * List keys matching a glob-style pattern.
115
+ * Supports * and ? wildcards.
116
+ */
117
+ async keys(pattern) {
118
+ const result = [];
119
+ const regex = pattern ? this.patternToRegex(pattern) : null;
120
+ const expiredKeys = [];
121
+ for (const [key, entry] of this.memory) {
122
+ if (this.isExpired(entry)) {
123
+ expiredKeys.push(key);
124
+ }
125
+ }
126
+ for (const key of expiredKeys) {
127
+ await this.delete(key);
128
+ }
129
+ for (const [key, entry] of this.memory) {
130
+ if (this.isExpired(entry)) {
131
+ continue;
132
+ }
133
+ if (regex && !regex.test(key)) {
134
+ continue;
135
+ }
136
+ result.push(key);
137
+ }
138
+ return result;
139
+ }
140
+ /**
141
+ * Gracefully close the provider.
142
+ */
143
+ async close() {
144
+ if (this.sweeper) clearInterval(this.sweeper);
145
+ for (const [, entry] of this.memory) {
146
+ if (entry.timeout) clearTimeout(entry.timeout);
147
+ }
148
+ this.memory.clear();
149
+ }
150
+ // ─────────────────────────────────────────────────────────────────────────────
151
+ // Private Methods
152
+ // ─────────────────────────────────────────────────────────────────────────────
153
+ isExpired(entry) {
154
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
155
+ }
156
+ /**
157
+ * Periodically remove expired keys to keep memory tidy.
158
+ */
159
+ sweep() {
160
+ const now = Date.now();
161
+ for (const [key, entry] of this.memory) {
162
+ if (entry.expiresAt !== void 0 && entry.expiresAt <= now) {
163
+ if (entry.timeout) clearTimeout(entry.timeout);
164
+ this.memory.delete(key);
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Convert a glob-style pattern to a RegExp.
170
+ * * matches any sequence of characters
171
+ * ? matches any single character
172
+ *
173
+ * Includes ReDoS protection: validates pattern length and wildcard count.
174
+ */
175
+ patternToRegex(pattern) {
176
+ if (pattern.length > RememberMemoryProvider.MAX_PATTERN_LENGTH) {
177
+ throw new Error(`Pattern too long: ${pattern.length} chars (max ${RememberMemoryProvider.MAX_PATTERN_LENGTH})`);
178
+ }
179
+ const wildcardCount = (pattern.match(/[*?]/g) || []).length;
180
+ if (wildcardCount > RememberMemoryProvider.MAX_WILDCARDS) {
181
+ throw new Error(`Too many wildcards in pattern: ${wildcardCount} (max ${RememberMemoryProvider.MAX_WILDCARDS})`);
182
+ }
183
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
184
+ return new RegExp(`^${escaped}$`);
185
+ }
186
+ };
187
+ // ReDoS protection constants
188
+ // Pattern length allows for long prefixes (e.g., remember:session:{uuid}:user:*)
189
+ __publicField(RememberMemoryProvider, "MAX_PATTERN_LENGTH", 500);
190
+ __publicField(RememberMemoryProvider, "MAX_WILDCARDS", 20);
191
+ RememberMemoryProvider = __decorateClass([
192
+ Provider({
193
+ name: "provider:remember:memory",
194
+ description: "In-memory storage provider for RememberPlugin",
195
+ scope: ProviderScope.GLOBAL
196
+ })
197
+ ], RememberMemoryProvider);
198
+
199
+ // plugins/plugin-remember/src/providers/remember-redis.provider.ts
200
+ import Redis from "ioredis";
201
+ import { Provider as Provider2, ProviderScope as ProviderScope2 } from "@frontmcp/sdk";
202
+ var RememberRedisProvider = class {
203
+ client;
204
+ /**
205
+ * Prefix prepended to all Redis keys.
206
+ * Include any separator (e.g., "myapp:" or "user:123:") as part of the prefix.
207
+ */
208
+ keyPrefix;
209
+ /** True if this provider created the client (and should close it), false if externally provided */
210
+ ownsClient;
211
+ constructor(options) {
212
+ this.keyPrefix = options.keyPrefix ?? "";
213
+ if (options.type === "redis-client") {
214
+ this.client = options.client;
215
+ this.ownsClient = false;
216
+ return;
217
+ }
218
+ this.ownsClient = true;
219
+ this.client = new Redis({
220
+ lazyConnect: false,
221
+ maxRetriesPerRequest: 3,
222
+ ...options.config
223
+ });
224
+ this.client.on("connect", () => {
225
+ if (process.env["DEBUG"] === "true") {
226
+ console.log("[RememberPlugin:Redis] Connected");
227
+ }
228
+ });
229
+ this.client.on("error", (err) => {
230
+ console.error("[RememberPlugin:Redis] Error:", err.message);
231
+ });
232
+ }
233
+ /**
234
+ * Store a value with optional TTL.
235
+ *
236
+ * @param key - The key to store under
237
+ * @param value - The value to store (must not be undefined)
238
+ * @param ttlSeconds - Optional TTL in seconds (must be positive integer if provided)
239
+ * @throws Error if value is undefined or ttlSeconds is invalid
240
+ */
241
+ async setValue(key, value, ttlSeconds) {
242
+ if (value === void 0) {
243
+ throw new Error("Cannot store undefined value. Use null or delete the key instead.");
244
+ }
245
+ if (ttlSeconds !== void 0) {
246
+ if (typeof ttlSeconds !== "number" || !Number.isFinite(ttlSeconds)) {
247
+ throw new Error(`Invalid TTL: expected a number, got ${typeof ttlSeconds}`);
248
+ }
249
+ if (ttlSeconds <= 0) {
250
+ throw new Error(`Invalid TTL: must be positive, got ${ttlSeconds}`);
251
+ }
252
+ if (!Number.isInteger(ttlSeconds)) {
253
+ throw new Error(`Invalid TTL: must be an integer, got ${ttlSeconds}`);
254
+ }
255
+ }
256
+ const fullKey = this.keyPrefix + key;
257
+ const strValue = typeof value === "string" ? value : JSON.stringify(value);
258
+ if (ttlSeconds !== void 0 && ttlSeconds > 0) {
259
+ await this.client.set(fullKey, strValue, "EX", ttlSeconds);
260
+ } else {
261
+ await this.client.set(fullKey, strValue);
262
+ }
263
+ }
264
+ /**
265
+ * Retrieve a value by key.
266
+ *
267
+ * Returns the parsed JSON value if successful, or `defaultValue` if:
268
+ * - The key does not exist
269
+ * - The stored value is not valid JSON (malformed or legacy data)
270
+ *
271
+ * @param key - The key to retrieve
272
+ * @param defaultValue - Value to return if key doesn't exist or parsing fails
273
+ * @returns The parsed value as T, or defaultValue/undefined
274
+ */
275
+ async getValue(key, defaultValue) {
276
+ const fullKey = this.keyPrefix + key;
277
+ const raw = await this.client.get(fullKey);
278
+ if (raw === null) return defaultValue;
279
+ try {
280
+ return JSON.parse(raw);
281
+ } catch {
282
+ return defaultValue;
283
+ }
284
+ }
285
+ /**
286
+ * Delete a key.
287
+ */
288
+ async delete(key) {
289
+ const fullKey = this.keyPrefix + key;
290
+ await this.client.del(fullKey);
291
+ }
292
+ /**
293
+ * Check if a key exists.
294
+ */
295
+ async exists(key) {
296
+ const fullKey = this.keyPrefix + key;
297
+ return await this.client.exists(fullKey) === 1;
298
+ }
299
+ /**
300
+ * List keys matching a pattern.
301
+ * Uses Redis SCAN for efficient iteration.
302
+ */
303
+ async keys(pattern) {
304
+ const searchPattern = this.keyPrefix + (pattern ?? "*");
305
+ const result = [];
306
+ let cursor = "0";
307
+ do {
308
+ const [nextCursor, keys] = await this.client.scan(cursor, "MATCH", searchPattern, "COUNT", 100);
309
+ cursor = nextCursor;
310
+ for (const key of keys) {
311
+ result.push(key.slice(this.keyPrefix.length));
312
+ }
313
+ } while (cursor !== "0");
314
+ return result;
315
+ }
316
+ /**
317
+ * Gracefully close the Redis connection.
318
+ * Only closes if this provider owns the client (created it internally).
319
+ * Externally-provided clients are left open for the caller to manage.
320
+ */
321
+ async close() {
322
+ if (this.ownsClient) {
323
+ await this.client.quit();
324
+ }
325
+ }
326
+ };
327
+ RememberRedisProvider = __decorateClass([
328
+ Provider2({
329
+ name: "provider:remember:redis",
330
+ description: "Redis-based storage provider for RememberPlugin",
331
+ scope: ProviderScope2.GLOBAL
332
+ })
333
+ ], RememberRedisProvider);
334
+
335
+ // plugins/plugin-remember/src/providers/remember-vercel-kv.provider.ts
336
+ import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmcp/sdk";
337
+ var RememberVercelKvProvider = class {
338
+ kv;
339
+ keyPrefix;
340
+ defaultTTL;
341
+ constructor(options = {}) {
342
+ const vercelKv = __require("@vercel/kv");
343
+ const hasUrl = options.url !== void 0;
344
+ const hasToken = options.token !== void 0;
345
+ if (hasUrl !== hasToken) {
346
+ throw new Error(
347
+ `RememberVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. Received: url=${hasUrl ? "provided" : "missing"}, token=${hasToken ? "provided" : "missing"}`
348
+ );
349
+ }
350
+ if (options.url && options.token) {
351
+ this.kv = vercelKv.createClient({
352
+ url: options.url,
353
+ token: options.token
354
+ });
355
+ } else {
356
+ this.kv = vercelKv.kv;
357
+ }
358
+ this.keyPrefix = options.keyPrefix ?? "remember:";
359
+ this.defaultTTL = options.defaultTTL;
360
+ }
361
+ prefixKey(key) {
362
+ return `${this.keyPrefix}${key}`;
363
+ }
364
+ /**
365
+ * Store a value with optional TTL.
366
+ */
367
+ async setValue(key, value, ttlSeconds) {
368
+ const fullKey = this.prefixKey(key);
369
+ const strValue = typeof value === "string" ? value : JSON.stringify(value);
370
+ const ttl = ttlSeconds ?? this.defaultTTL;
371
+ if (ttl && ttl > 0) {
372
+ await this.kv.set(fullKey, strValue, { ex: ttl });
373
+ } else {
374
+ await this.kv.set(fullKey, strValue);
375
+ }
376
+ }
377
+ /**
378
+ * Retrieve a value by key.
379
+ */
380
+ async getValue(key, defaultValue) {
381
+ const fullKey = this.prefixKey(key);
382
+ const raw = await this.kv.get(fullKey);
383
+ if (raw === null || raw === void 0) return defaultValue;
384
+ if (typeof raw === "string") {
385
+ try {
386
+ return JSON.parse(raw);
387
+ } catch {
388
+ return raw;
389
+ }
390
+ }
391
+ return raw;
392
+ }
393
+ /**
394
+ * Delete a key.
395
+ */
396
+ async delete(key) {
397
+ const fullKey = this.prefixKey(key);
398
+ await this.kv.del(fullKey);
399
+ }
400
+ /**
401
+ * Check if a key exists.
402
+ */
403
+ async exists(key) {
404
+ const fullKey = this.prefixKey(key);
405
+ return await this.kv.exists(fullKey) === 1;
406
+ }
407
+ /**
408
+ * List keys matching a pattern.
409
+ * Uses SCAN for efficient iteration.
410
+ */
411
+ async keys(pattern) {
412
+ const searchPattern = this.prefixKey(pattern ?? "*");
413
+ const result = [];
414
+ try {
415
+ let cursor = 0;
416
+ do {
417
+ const [nextCursor, keys] = await this.kv.scan(cursor, {
418
+ match: searchPattern,
419
+ count: 100
420
+ });
421
+ cursor = nextCursor;
422
+ for (const key of keys) {
423
+ result.push(key.slice(this.keyPrefix.length));
424
+ }
425
+ } while (cursor !== 0);
426
+ } catch {
427
+ try {
428
+ const keys = await this.kv.keys(searchPattern);
429
+ for (const key of keys) {
430
+ result.push(key.slice(this.keyPrefix.length));
431
+ }
432
+ } catch {
433
+ console.warn("[RememberPlugin:VercelKV] keys() operation not supported");
434
+ }
435
+ }
436
+ return result;
437
+ }
438
+ /**
439
+ * Gracefully close the provider.
440
+ * No-op for Vercel KV as it uses stateless REST API.
441
+ */
442
+ async close() {
443
+ }
444
+ };
445
+ RememberVercelKvProvider = __decorateClass([
446
+ Provider3({
447
+ name: "provider:remember:vercel-kv",
448
+ description: "Vercel KV-based storage provider for RememberPlugin",
449
+ scope: ProviderScope3.GLOBAL
450
+ })
451
+ ], RememberVercelKvProvider);
452
+
453
+ // plugins/plugin-remember/src/providers/remember-accessor.provider.ts
454
+ import { Provider as Provider4, ProviderScope as ProviderScope4 } from "@frontmcp/sdk";
455
+
456
+ // plugins/plugin-remember/src/remember.crypto.ts
457
+ import {
458
+ hkdfSha256,
459
+ encryptAesGcm,
460
+ decryptAesGcm,
461
+ randomBytes as randomBytes2,
462
+ base64urlEncode as base64urlEncode2,
463
+ base64urlDecode
464
+ } from "@frontmcp/utils";
465
+
466
+ // plugins/plugin-remember/src/remember.secret-persistence.ts
467
+ import * as path from "path";
468
+ import { z } from "zod";
469
+ import { randomBytes, base64urlEncode, readFile, mkdir, writeFile, rename, unlink } from "@frontmcp/utils";
470
+ var DEFAULT_SECRET_FILE_PATH = ".frontmcp/remember-secret.json";
471
+ var SECRET_BYTES = 32;
472
+ var rememberSecretDataSchema = z.object({
473
+ secret: z.string().min(32),
474
+ // base64url of 32 bytes is ~43 chars
475
+ createdAt: z.number().positive().int(),
476
+ version: z.literal(1)
477
+ });
478
+ function validateSecretData(data) {
479
+ const result = rememberSecretDataSchema.safeParse(data);
480
+ if (!result.success) {
481
+ return { valid: false, error: result.error.issues[0]?.message ?? "Invalid secret structure" };
482
+ }
483
+ const parsed = result.data;
484
+ const now = Date.now();
485
+ if (parsed.createdAt > now + 6e4) {
486
+ return { valid: false, error: "createdAt is in the future" };
487
+ }
488
+ const hundredYearsMs = 100 * 365 * 24 * 60 * 60 * 1e3;
489
+ if (parsed.createdAt < now - hundredYearsMs) {
490
+ return { valid: false, error: "createdAt is too old" };
491
+ }
492
+ return { valid: true };
493
+ }
494
+ function isSecretPersistenceEnabled(options) {
495
+ const isProduction = process.env["NODE_ENV"] === "production";
496
+ if (isProduction) {
497
+ return options?.forceEnable === true;
498
+ }
499
+ return true;
500
+ }
501
+ function resolveSecretPath(options) {
502
+ const secretPath = options?.secretPath ?? DEFAULT_SECRET_FILE_PATH;
503
+ if (path.isAbsolute(secretPath)) {
504
+ return secretPath;
505
+ }
506
+ return path.resolve(process.cwd(), secretPath);
507
+ }
508
+ async function loadRememberSecret(options) {
509
+ if (!isSecretPersistenceEnabled(options)) {
510
+ return null;
511
+ }
512
+ const secretPath = resolveSecretPath(options);
513
+ try {
514
+ const content = await readFile(secretPath, "utf8");
515
+ const data = JSON.parse(content);
516
+ const validation = validateSecretData(data);
517
+ if (!validation.valid) {
518
+ console.warn(
519
+ `[RememberSecretPersistence] Invalid secret file format at ${secretPath}: ${validation.error}, will regenerate`
520
+ );
521
+ return null;
522
+ }
523
+ return data;
524
+ } catch (error) {
525
+ if (error.code === "ENOENT") {
526
+ return null;
527
+ }
528
+ console.warn(`[RememberSecretPersistence] Failed to load secret from ${secretPath}: ${error.message}`);
529
+ return null;
530
+ }
531
+ }
532
+ async function saveRememberSecret(secretData, options) {
533
+ if (!isSecretPersistenceEnabled(options)) {
534
+ return true;
535
+ }
536
+ const secretPath = resolveSecretPath(options);
537
+ const dir = path.dirname(secretPath);
538
+ const tempPath = `${secretPath}.tmp.${Date.now()}.${base64urlEncode(randomBytes(8))}`;
539
+ try {
540
+ await mkdir(dir, { recursive: true, mode: 448 });
541
+ const content = JSON.stringify(secretData, null, 2);
542
+ await writeFile(tempPath, content, { mode: 384 });
543
+ await rename(tempPath, secretPath);
544
+ return true;
545
+ } catch (error) {
546
+ console.error(`[RememberSecretPersistence] Failed to save secret to ${secretPath}: ${error.message}`);
547
+ try {
548
+ await unlink(tempPath);
549
+ } catch {
550
+ }
551
+ return false;
552
+ }
553
+ }
554
+ var cachedSecret = null;
555
+ var secretGenerationPromise = null;
556
+ function generateSecret() {
557
+ return base64urlEncode(randomBytes(SECRET_BYTES));
558
+ }
559
+ async function getOrCreatePersistedSecret(options) {
560
+ if (cachedSecret) {
561
+ return cachedSecret;
562
+ }
563
+ if (secretGenerationPromise) {
564
+ return secretGenerationPromise;
565
+ }
566
+ secretGenerationPromise = (async () => {
567
+ try {
568
+ const loaded = await loadRememberSecret(options);
569
+ if (loaded) {
570
+ cachedSecret = loaded.secret;
571
+ return cachedSecret;
572
+ }
573
+ const newSecret = generateSecret();
574
+ const secretData = {
575
+ secret: newSecret,
576
+ createdAt: Date.now(),
577
+ version: 1
578
+ };
579
+ if (isSecretPersistenceEnabled(options)) {
580
+ await saveRememberSecret(secretData, options);
581
+ }
582
+ cachedSecret = newSecret;
583
+ return cachedSecret;
584
+ } finally {
585
+ secretGenerationPromise = null;
586
+ }
587
+ })();
588
+ return secretGenerationPromise;
589
+ }
590
+
591
+ // plugins/plugin-remember/src/remember.crypto.ts
592
+ async function getBaseSecret() {
593
+ const envSecret = process.env["REMEMBER_SECRET"] || process.env["MCP_MEMORY_SECRET"] || process.env["MCP_SESSION_SECRET"];
594
+ if (envSecret) {
595
+ return envSecret;
596
+ }
597
+ return getOrCreatePersistedSecret();
598
+ }
599
+ var textEncoder = new TextEncoder();
600
+ async function deriveEncryptionKey(source) {
601
+ const salt = textEncoder.encode("remember-plugin-v1");
602
+ let ikm;
603
+ let context;
604
+ switch (source.type) {
605
+ case "session":
606
+ ikm = source.sessionId;
607
+ context = `remember:session:${source.sessionId}`;
608
+ break;
609
+ case "tool":
610
+ ikm = source.sessionId;
611
+ context = `remember:tool:${source.toolName}:${source.sessionId}`;
612
+ break;
613
+ case "user":
614
+ ikm = await getBaseSecret() + source.userId;
615
+ context = `remember:user:${source.userId}`;
616
+ break;
617
+ case "global":
618
+ ikm = await getBaseSecret();
619
+ context = "remember:global";
620
+ break;
621
+ case "custom":
622
+ ikm = source.key;
623
+ context = `remember:custom:${source.key}`;
624
+ break;
625
+ }
626
+ const ikmBytes = textEncoder.encode(ikm);
627
+ const info = textEncoder.encode(context);
628
+ return hkdfSha256(ikmBytes, salt, info, 32);
629
+ }
630
+ function getKeySourceForScope(scope, context) {
631
+ switch (scope) {
632
+ case "session":
633
+ return { type: "session", sessionId: context.sessionId };
634
+ case "user":
635
+ return { type: "user", userId: context.userId ?? "anonymous" };
636
+ case "tool":
637
+ return {
638
+ type: "tool",
639
+ toolName: context.toolName ?? "unknown",
640
+ sessionId: context.sessionId
641
+ };
642
+ case "global":
643
+ return { type: "global" };
644
+ }
645
+ }
646
+ var textDecoder = new TextDecoder();
647
+ async function encryptValue(data, keySource) {
648
+ const key = await deriveEncryptionKey(keySource);
649
+ const plaintext = textEncoder.encode(JSON.stringify(data));
650
+ const iv = randomBytes2(12);
651
+ const { ciphertext, tag } = encryptAesGcm(key, plaintext, iv);
652
+ return {
653
+ alg: "A256GCM",
654
+ iv: base64urlEncode2(iv),
655
+ tag: base64urlEncode2(tag),
656
+ data: base64urlEncode2(ciphertext)
657
+ };
658
+ }
659
+ async function decryptValue(blob, keySource) {
660
+ try {
661
+ const key = await deriveEncryptionKey(keySource);
662
+ const iv = base64urlDecode(blob.iv);
663
+ const tag = base64urlDecode(blob.tag);
664
+ const ciphertext = base64urlDecode(blob.data);
665
+ const decrypted = decryptAesGcm(key, ciphertext, iv, tag);
666
+ return JSON.parse(textDecoder.decode(decrypted));
667
+ } catch {
668
+ return null;
669
+ }
670
+ }
671
+ function serializeBlob(blob) {
672
+ return JSON.stringify(blob);
673
+ }
674
+ function deserializeBlob(str) {
675
+ try {
676
+ const parsed = JSON.parse(str);
677
+ if (parsed && typeof parsed === "object" && parsed.alg === "A256GCM" && typeof parsed.iv === "string" && typeof parsed.tag === "string" && typeof parsed.data === "string") {
678
+ return parsed;
679
+ }
680
+ return null;
681
+ } catch {
682
+ return null;
683
+ }
684
+ }
685
+ async function encryptAndSerialize(data, keySource) {
686
+ const blob = await encryptValue(data, keySource);
687
+ return serializeBlob(blob);
688
+ }
689
+ async function deserializeAndDecrypt(str, keySource) {
690
+ const blob = deserializeBlob(str);
691
+ if (!blob) return null;
692
+ return decryptValue(blob, keySource);
693
+ }
694
+
695
+ // plugins/plugin-remember/src/providers/remember-accessor.provider.ts
696
+ var RememberAccessor = class {
697
+ store;
698
+ ctx;
699
+ config;
700
+ keyPrefix;
701
+ encryptionEnabled;
702
+ constructor(store, ctx, config) {
703
+ this.store = store;
704
+ this.ctx = ctx;
705
+ this.config = config;
706
+ this.keyPrefix = config.keyPrefix ?? "remember:";
707
+ this.encryptionEnabled = config.encryption?.enabled !== false;
708
+ }
709
+ // ─────────────────────────────────────────────────────────────────────────────
710
+ // Public API
711
+ // ─────────────────────────────────────────────────────────────────────────────
712
+ /**
713
+ * Store a value in memory.
714
+ *
715
+ * @param key - The key to store under
716
+ * @param value - The value to store (any JSON-serializable data)
717
+ * @param options - Storage options (scope, ttl, brand, metadata)
718
+ */
719
+ async set(key, value, options = {}) {
720
+ const scope = options.scope ?? "session";
721
+ const storageKey = this.buildStorageKey(key, scope);
722
+ const entry = {
723
+ value,
724
+ brand: options.brand,
725
+ createdAt: Date.now(),
726
+ updatedAt: Date.now(),
727
+ expiresAt: options.ttl ? Date.now() + options.ttl * 1e3 : void 0,
728
+ metadata: options.metadata
729
+ };
730
+ const serialized = this.encryptionEnabled ? await encryptAndSerialize(entry, this.getKeySource(scope)) : JSON.stringify(entry);
731
+ await this.store.setValue(storageKey, serialized, options.ttl);
732
+ }
733
+ /**
734
+ * Retrieve a value from memory.
735
+ *
736
+ * @param key - The key to retrieve
737
+ * @param options - Retrieval options (scope, defaultValue)
738
+ * @returns The stored value or defaultValue if not found
739
+ */
740
+ async get(key, options = {}) {
741
+ const scope = options.scope ?? "session";
742
+ const storageKey = this.buildStorageKey(key, scope);
743
+ const raw = await this.store.getValue(storageKey);
744
+ if (!raw) return options.defaultValue;
745
+ const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
746
+ if (!entry) return options.defaultValue;
747
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
748
+ await this.forget(key, { scope });
749
+ return options.defaultValue;
750
+ }
751
+ return entry.value;
752
+ }
753
+ /**
754
+ * Get the full entry including metadata.
755
+ *
756
+ * @param key - The key to retrieve
757
+ * @param options - Retrieval options (scope)
758
+ * @returns The full entry or undefined if not found
759
+ */
760
+ async getEntry(key, options = {}) {
761
+ const scope = options.scope ?? "session";
762
+ const storageKey = this.buildStorageKey(key, scope);
763
+ const raw = await this.store.getValue(storageKey);
764
+ if (!raw) return void 0;
765
+ const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
766
+ if (!entry) return void 0;
767
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
768
+ await this.forget(key, { scope });
769
+ return void 0;
770
+ }
771
+ return entry;
772
+ }
773
+ /**
774
+ * Forget (delete) a value from memory.
775
+ *
776
+ * @param key - The key to forget
777
+ * @param options - Options (scope)
778
+ */
779
+ async forget(key, options = {}) {
780
+ const scope = options.scope ?? "session";
781
+ const storageKey = this.buildStorageKey(key, scope);
782
+ await this.store.delete(storageKey);
783
+ }
784
+ /**
785
+ * Check if a key is remembered (exists and not expired).
786
+ *
787
+ * @param key - The key to check
788
+ * @param options - Options (scope)
789
+ * @returns true if the key exists
790
+ */
791
+ async knows(key, options = {}) {
792
+ const scope = options.scope ?? "session";
793
+ const storageKey = this.buildStorageKey(key, scope);
794
+ return this.store.exists(storageKey);
795
+ }
796
+ /**
797
+ * List all remembered keys for a scope.
798
+ *
799
+ * @param options - Options (scope, pattern)
800
+ * @returns Array of keys (without the scope prefix)
801
+ */
802
+ async list(options = {}) {
803
+ const scope = options.scope ?? "session";
804
+ const scopePrefix = this.buildScopePrefix(scope);
805
+ const fullPattern = scopePrefix + (options.pattern ?? "*");
806
+ const keys = await this.store.keys(fullPattern);
807
+ return keys.map((k) => k.slice(scopePrefix.length));
808
+ }
809
+ /**
810
+ * Update an existing entry's value while preserving metadata.
811
+ *
812
+ * @param key - The key to update
813
+ * @param value - The new value
814
+ * @param options - Options (scope, ttl)
815
+ */
816
+ async update(key, value, options = {}) {
817
+ const scope = options.scope ?? "session";
818
+ const existing = await this.getEntry(key, { scope });
819
+ if (!existing) return false;
820
+ const entry = {
821
+ ...existing,
822
+ value,
823
+ updatedAt: Date.now(),
824
+ expiresAt: options.ttl ? Date.now() + options.ttl * 1e3 : existing.expiresAt
825
+ };
826
+ const storageKey = this.buildStorageKey(key, scope);
827
+ const serialized = this.encryptionEnabled ? await encryptAndSerialize(entry, this.getKeySource(scope)) : JSON.stringify(entry);
828
+ await this.store.setValue(storageKey, serialized, options.ttl);
829
+ return true;
830
+ }
831
+ // ─────────────────────────────────────────────────────────────────────────────
832
+ // Convenience Methods
833
+ // ─────────────────────────────────────────────────────────────────────────────
834
+ /**
835
+ * Get the session ID from context.
836
+ */
837
+ get sessionId() {
838
+ return this.ctx.sessionId;
839
+ }
840
+ /**
841
+ * Get the user ID from context (if available).
842
+ */
843
+ get userId() {
844
+ const authInfo = this.ctx.authInfo;
845
+ return authInfo?.extra?.["userId"] ?? authInfo?.extra?.["sub"] ?? authInfo?.clientId ?? void 0;
846
+ }
847
+ // ─────────────────────────────────────────────────────────────────────────────
848
+ // Private Methods
849
+ // ─────────────────────────────────────────────────────────────────────────────
850
+ /**
851
+ * Build the full storage key including scope prefix.
852
+ */
853
+ buildStorageKey(key, scope) {
854
+ return this.buildScopePrefix(scope) + key;
855
+ }
856
+ /**
857
+ * Build the scope-specific prefix.
858
+ */
859
+ buildScopePrefix(scope) {
860
+ switch (scope) {
861
+ case "session":
862
+ return `${this.keyPrefix}session:${this.ctx.sessionId}:`;
863
+ case "user":
864
+ return `${this.keyPrefix}user:${this.userId ?? "anonymous"}:`;
865
+ case "tool": {
866
+ const toolName = this.ctx.flow?.name ?? "unknown";
867
+ return `${this.keyPrefix}tool:${toolName}:${this.ctx.sessionId}:`;
868
+ }
869
+ case "global":
870
+ return `${this.keyPrefix}global:`;
871
+ }
872
+ }
873
+ /**
874
+ * Get the encryption key source for a scope.
875
+ */
876
+ getKeySource(scope) {
877
+ return getKeySourceForScope(scope, {
878
+ sessionId: this.ctx.sessionId,
879
+ userId: this.userId,
880
+ toolName: this.ctx.flow?.name
881
+ });
882
+ }
883
+ /**
884
+ * Parse a JSON entry (when encryption is disabled).
885
+ */
886
+ parseEntry(raw) {
887
+ try {
888
+ return JSON.parse(raw);
889
+ } catch {
890
+ return null;
891
+ }
892
+ }
893
+ };
894
+ RememberAccessor = __decorateClass([
895
+ Provider4({
896
+ name: "provider:remember:accessor",
897
+ description: "Context-scoped accessor for RememberPlugin storage",
898
+ scope: ProviderScope4.CONTEXT
899
+ })
900
+ ], RememberAccessor);
901
+ function createRememberAccessor(store, ctx, config) {
902
+ return new RememberAccessor(store, ctx, config);
903
+ }
904
+
905
+ // plugins/plugin-remember/src/remember.plugin.ts
906
+ var RememberPlugin = class extends DynamicPlugin {
907
+ options;
908
+ constructor(options = {}) {
909
+ super();
910
+ this.options = {
911
+ ...RememberPlugin.defaultOptions,
912
+ ...options
913
+ };
914
+ }
915
+ };
916
+ __publicField(RememberPlugin, "defaultOptions", {
917
+ type: "memory",
918
+ keyPrefix: "remember:",
919
+ encryption: { enabled: true }
920
+ });
921
+ /**
922
+ * Dynamic providers based on plugin options.
923
+ */
924
+ __publicField(RememberPlugin, "dynamicProviders", (options) => {
925
+ const providers = [];
926
+ const config = {
927
+ ...RememberPlugin.defaultOptions,
928
+ ...options
929
+ };
930
+ switch (config.type) {
931
+ case "global-store":
932
+ providers.push({
933
+ name: "remember:store:global",
934
+ provide: RememberStoreToken,
935
+ inject: () => [FrontMcpConfig],
936
+ useFactory: (frontmcpConfig) => {
937
+ const storeConfig = getGlobalStoreConfig("RememberPlugin", frontmcpConfig);
938
+ if (isVercelKvProvider(storeConfig)) {
939
+ return new RememberVercelKvProvider({
940
+ url: storeConfig.url,
941
+ token: storeConfig.token,
942
+ keyPrefix: config.keyPrefix,
943
+ defaultTTL: config.defaultTTL
944
+ });
945
+ }
946
+ return new RememberRedisProvider({
947
+ type: "redis",
948
+ config: {
949
+ host: storeConfig.host ?? "localhost",
950
+ port: storeConfig.port ?? 6379,
951
+ password: storeConfig.password,
952
+ db: storeConfig.db
953
+ },
954
+ keyPrefix: config.keyPrefix,
955
+ defaultTTL: config.defaultTTL
956
+ });
957
+ }
958
+ });
959
+ break;
960
+ case "redis":
961
+ if ("config" in config && config.config) {
962
+ providers.push({
963
+ name: "remember:store:redis",
964
+ provide: RememberStoreToken,
965
+ useValue: new RememberRedisProvider({
966
+ type: "redis",
967
+ config: config.config,
968
+ keyPrefix: config.keyPrefix,
969
+ defaultTTL: config.defaultTTL
970
+ })
971
+ });
972
+ }
973
+ break;
974
+ case "redis-client":
975
+ if ("client" in config && config.client) {
976
+ providers.push({
977
+ name: "remember:store:redis-client",
978
+ provide: RememberStoreToken,
979
+ useValue: new RememberRedisProvider({
980
+ type: "redis-client",
981
+ client: config.client,
982
+ keyPrefix: config.keyPrefix,
983
+ defaultTTL: config.defaultTTL
984
+ })
985
+ });
986
+ }
987
+ break;
988
+ case "vercel-kv":
989
+ providers.push({
990
+ name: "remember:store:vercel-kv",
991
+ provide: RememberStoreToken,
992
+ useValue: new RememberVercelKvProvider({
993
+ url: "url" in config ? config.url : void 0,
994
+ token: "token" in config ? config.token : void 0,
995
+ keyPrefix: config.keyPrefix,
996
+ defaultTTL: config.defaultTTL
997
+ })
998
+ });
999
+ break;
1000
+ case "memory":
1001
+ default:
1002
+ providers.push({
1003
+ name: "remember:store:memory",
1004
+ provide: RememberStoreToken,
1005
+ useValue: new RememberMemoryProvider()
1006
+ });
1007
+ break;
1008
+ }
1009
+ providers.push({
1010
+ name: "remember:config",
1011
+ provide: RememberConfigToken,
1012
+ useValue: config
1013
+ });
1014
+ providers.push({
1015
+ name: "remember:accessor",
1016
+ provide: RememberAccessorToken,
1017
+ scope: ProviderScope5.CONTEXT,
1018
+ inject: () => [RememberStoreToken, FRONTMCP_CONTEXT2, RememberConfigToken],
1019
+ useFactory: (store, ctx, cfg) => createRememberAccessor(store, ctx, cfg)
1020
+ });
1021
+ return providers;
1022
+ });
1023
+ RememberPlugin = __decorateClass([
1024
+ Plugin({
1025
+ name: "remember",
1026
+ description: "Help your LLM remember things across sessions",
1027
+ providers: [
1028
+ // Default in-memory provider (overridden by dynamicProviders if configured)
1029
+ {
1030
+ name: "remember:store:memory",
1031
+ provide: RememberStoreToken,
1032
+ useValue: new RememberMemoryProvider()
1033
+ }
1034
+ ],
1035
+ // Context extensions - SDK handles runtime installation
1036
+ // TypeScript types are declared in remember.context-extension.ts
1037
+ contextExtensions: [
1038
+ {
1039
+ property: "remember",
1040
+ token: RememberAccessorToken,
1041
+ errorMessage: "RememberPlugin is not installed. Add RememberPlugin.init() to your plugins array."
1042
+ }
1043
+ ]
1044
+ })
1045
+ ], RememberPlugin);
1046
+
1047
+ // plugins/plugin-remember/src/remember.types.ts
1048
+ import { z as z2 } from "zod";
1049
+ var rememberScopeSchema = z2.enum(["session", "user", "tool", "global"]);
1050
+ function brand(data, _brand) {
1051
+ return data;
1052
+ }
1053
+ var rememberEntrySchema = z2.object({
1054
+ value: z2.unknown(),
1055
+ brand: z2.enum(["approval", "preference", "cache", "state", "conversation", "custom"]).optional(),
1056
+ createdAt: z2.number(),
1057
+ updatedAt: z2.number(),
1058
+ expiresAt: z2.number().optional(),
1059
+ metadata: z2.record(z2.string(), z2.unknown()).optional()
1060
+ });
1061
+
1062
+ // plugins/plugin-remember/src/tools/remember-this.tool.ts
1063
+ import { Tool, ToolContext } from "@frontmcp/sdk";
1064
+ import { z as z3 } from "zod";
1065
+ var rememberThisInputSchema = z3.object({
1066
+ key: z3.string().min(1).describe('What to call this memory (e.g., "user_preference", "last_action")'),
1067
+ value: z3.unknown().describe("The value to remember (any JSON-serializable data)"),
1068
+ scope: z3.enum(["session", "user", "tool", "global"]).optional().describe(
1069
+ "How long to remember: session (until disconnect), user (forever), tool (for this tool), global (shared)"
1070
+ ),
1071
+ ttl: z3.number().positive().optional().describe("Forget after this many seconds"),
1072
+ brand: z3.enum(["preference", "cache", "state", "conversation", "custom"]).optional().describe("Type hint for the stored value")
1073
+ });
1074
+ var rememberThisOutputSchema = z3.object({
1075
+ success: z3.boolean(),
1076
+ key: z3.string(),
1077
+ scope: z3.string(),
1078
+ expiresAt: z3.number().optional()
1079
+ });
1080
+ var RememberThisTool = class extends ToolContext {
1081
+ async execute(input) {
1082
+ const remember = this.get(RememberAccessorToken);
1083
+ const config = this.get(RememberConfigToken);
1084
+ const scope = input.scope ?? "session";
1085
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1086
+ if (!allowedScopes.includes(scope)) {
1087
+ throw this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1088
+ }
1089
+ await remember.set(input.key, input.value, {
1090
+ scope,
1091
+ ttl: input.ttl,
1092
+ brand: input.brand
1093
+ });
1094
+ const expiresAt = input.ttl ? Date.now() + input.ttl * 1e3 : void 0;
1095
+ return {
1096
+ success: true,
1097
+ key: input.key,
1098
+ scope,
1099
+ expiresAt
1100
+ };
1101
+ }
1102
+ };
1103
+ RememberThisTool = __decorateClass([
1104
+ Tool({
1105
+ name: "remember_this",
1106
+ description: "Remember something for later. Store a value that can be recalled in future conversations. Use this when the user asks you to remember preferences, settings, or any information they want to persist.",
1107
+ inputSchema: rememberThisInputSchema,
1108
+ outputSchema: rememberThisOutputSchema,
1109
+ annotations: {
1110
+ readOnlyHint: false
1111
+ }
1112
+ })
1113
+ ], RememberThisTool);
1114
+
1115
+ // plugins/plugin-remember/src/tools/recall.tool.ts
1116
+ import { Tool as Tool2, ToolContext as ToolContext2 } from "@frontmcp/sdk";
1117
+ import { z as z4 } from "zod";
1118
+ var recallInputSchema = {
1119
+ key: z4.string().min(1).describe("What memory to recall"),
1120
+ scope: z4.enum(["session", "user", "tool", "global"]).optional().describe("Which scope to look in (default: session)")
1121
+ };
1122
+ var recallOutputSchema = z4.object({
1123
+ found: z4.boolean(),
1124
+ key: z4.string(),
1125
+ value: z4.unknown().optional(),
1126
+ scope: z4.string(),
1127
+ createdAt: z4.number().optional(),
1128
+ expiresAt: z4.number().optional()
1129
+ });
1130
+ var RecallTool = class extends ToolContext2 {
1131
+ async execute(input) {
1132
+ const remember = this.get(RememberAccessorToken);
1133
+ const config = this.get(RememberConfigToken);
1134
+ const scope = input.scope ?? "session";
1135
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1136
+ if (!allowedScopes.includes(scope)) {
1137
+ this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1138
+ }
1139
+ const entry = await remember.getEntry(input.key, { scope });
1140
+ if (!entry) {
1141
+ return {
1142
+ found: false,
1143
+ key: input.key,
1144
+ scope
1145
+ };
1146
+ }
1147
+ return {
1148
+ found: true,
1149
+ key: input.key,
1150
+ value: entry.value,
1151
+ scope,
1152
+ createdAt: entry.createdAt,
1153
+ expiresAt: entry.expiresAt
1154
+ };
1155
+ }
1156
+ };
1157
+ RecallTool = __decorateClass([
1158
+ Tool2({
1159
+ name: "recall",
1160
+ description: "Recall something that was previously remembered. Use this to retrieve stored preferences, settings, or any information that was saved with remember_this.",
1161
+ inputSchema: recallInputSchema,
1162
+ outputSchema: recallOutputSchema,
1163
+ annotations: {
1164
+ readOnlyHint: true
1165
+ }
1166
+ })
1167
+ ], RecallTool);
1168
+
1169
+ // plugins/plugin-remember/src/tools/forget.tool.ts
1170
+ import { Tool as Tool3, ToolContext as ToolContext3 } from "@frontmcp/sdk";
1171
+ import { z as z5 } from "zod";
1172
+ var forgetInputSchema = z5.object({
1173
+ key: z5.string().min(1).describe("What memory to forget"),
1174
+ scope: z5.enum(["session", "user", "tool", "global"]).optional().describe("Which scope to forget from (default: session)")
1175
+ });
1176
+ var forgetOutputSchema = z5.object({
1177
+ success: z5.boolean(),
1178
+ key: z5.string(),
1179
+ scope: z5.string(),
1180
+ existed: z5.boolean()
1181
+ });
1182
+ var ForgetTool = class extends ToolContext3 {
1183
+ async execute(input) {
1184
+ const remember = this.get(RememberAccessorToken);
1185
+ const config = this.get(RememberConfigToken);
1186
+ const scope = input.scope ?? "session";
1187
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1188
+ if (!allowedScopes.includes(scope)) {
1189
+ throw this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1190
+ }
1191
+ const existed = await remember.knows(input.key, { scope });
1192
+ await remember.forget(input.key, { scope });
1193
+ return {
1194
+ success: true,
1195
+ key: input.key,
1196
+ scope,
1197
+ existed
1198
+ };
1199
+ }
1200
+ };
1201
+ ForgetTool = __decorateClass([
1202
+ Tool3({
1203
+ name: "forget",
1204
+ description: "Forget a previously remembered value. Use this when the user wants to delete stored preferences or information.",
1205
+ inputSchema: forgetInputSchema,
1206
+ outputSchema: forgetOutputSchema,
1207
+ annotations: {
1208
+ readOnlyHint: false
1209
+ }
1210
+ })
1211
+ ], ForgetTool);
1212
+
1213
+ // plugins/plugin-remember/src/tools/list-memories.tool.ts
1214
+ import { Tool as Tool4, ToolContext as ToolContext4 } from "@frontmcp/sdk";
1215
+ import { z as z6 } from "zod";
1216
+ var listMemoriesInputSchema = z6.object({
1217
+ scope: z6.enum(["session", "user", "tool", "global"]).optional().describe("Which scope to list from (default: session)"),
1218
+ pattern: z6.string().optional().describe('Pattern to filter keys (e.g., "user_*")'),
1219
+ limit: z6.number().positive().max(100).optional().describe("Maximum number of keys to return (default: 50)")
1220
+ });
1221
+ var listMemoriesOutputSchema = z6.object({
1222
+ keys: z6.array(z6.string()),
1223
+ scope: z6.string(),
1224
+ count: z6.number(),
1225
+ truncated: z6.boolean()
1226
+ });
1227
+ var ListMemoriesTool = class extends ToolContext4 {
1228
+ async execute(input) {
1229
+ const remember = this.get(RememberAccessorToken);
1230
+ const config = this.get(RememberConfigToken);
1231
+ const scope = input.scope ?? "session";
1232
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1233
+ if (!allowedScopes.includes(scope)) {
1234
+ throw this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1235
+ }
1236
+ const limit = input.limit ?? 50;
1237
+ let keys = await remember.list({ scope, pattern: input.pattern });
1238
+ const truncated = keys.length > limit;
1239
+ if (truncated) {
1240
+ keys = keys.slice(0, limit);
1241
+ }
1242
+ return {
1243
+ keys,
1244
+ scope,
1245
+ count: keys.length,
1246
+ truncated
1247
+ };
1248
+ }
1249
+ };
1250
+ ListMemoriesTool = __decorateClass([
1251
+ Tool4({
1252
+ name: "list_memories",
1253
+ description: "List all remembered keys in a scope. Use this to see what memories are stored for the current session or user.",
1254
+ inputSchema: listMemoriesInputSchema,
1255
+ outputSchema: listMemoriesOutputSchema,
1256
+ annotations: {
1257
+ readOnlyHint: true
1258
+ }
1259
+ })
1260
+ ], ListMemoriesTool);
1261
+
1262
+ // plugins/plugin-remember/src/remember.context-extension.ts
1263
+ function getRemember(ctx) {
1264
+ return ctx.get(RememberAccessorToken);
1265
+ }
1266
+ function tryGetRemember(ctx) {
1267
+ if (typeof ctx.tryGet === "function") {
1268
+ return ctx.tryGet(RememberAccessorToken);
1269
+ }
1270
+ return void 0;
1271
+ }
1272
+ export {
1273
+ ForgetTool,
1274
+ ListMemoriesTool,
1275
+ RecallTool,
1276
+ RememberAccessor,
1277
+ RememberAccessorToken,
1278
+ RememberConfigToken,
1279
+ RememberMemoryProvider,
1280
+ RememberPlugin,
1281
+ RememberRedisProvider,
1282
+ RememberStoreToken,
1283
+ RememberThisTool,
1284
+ RememberVercelKvProvider,
1285
+ brand,
1286
+ RememberPlugin as default,
1287
+ getRemember,
1288
+ tryGetRemember
1289
+ };