mindcache 2.4.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,4 +1,9 @@
1
- import { z } from 'zod';
1
+ import * as syncProtocol from 'y-protocols/sync';
2
+ import * as encoding from 'lib0/encoding';
3
+ import * as decoding from 'lib0/decoding';
4
+ import * as Y from 'yjs';
5
+ import { IndexeddbPersistence } from 'y-indexeddb';
6
+ import diff from 'fast-diff';
2
7
  import { useState, useRef, useEffect } from 'react';
3
8
 
4
9
  var __defProp = Object.defineProperty;
@@ -180,7 +185,6 @@ var init_CloudAdapter = __esm({
180
185
  }
181
186
  }
182
187
  ws = null;
183
- queue = [];
184
188
  mindcache = null;
185
189
  unsubscribe = null;
186
190
  reconnectAttempts = 0;
@@ -188,49 +192,29 @@ var init_CloudAdapter = __esm({
188
192
  _state = "disconnected";
189
193
  listeners = {};
190
194
  token = null;
191
- /**
192
- * Set auth token (short-lived, from /api/ws-token)
193
- * Call this before connect() or use setTokenProvider for auto-refresh
194
- */
195
195
  setToken(token) {
196
196
  this.token = token;
197
197
  }
198
- /**
199
- * Set a function that returns a fresh token
200
- * Used for automatic token refresh on reconnect
201
- */
202
198
  setTokenProvider(provider) {
203
199
  this.config.tokenProvider = provider;
204
200
  }
205
- /**
206
- * Get current connection state
207
- */
208
201
  get state() {
209
202
  return this._state;
210
203
  }
211
- /**
212
- * Attach to a MindCache instance and start syncing
213
- */
214
204
  attach(mc) {
215
205
  if (this.mindcache) {
216
206
  this.detach();
217
207
  }
218
208
  this.mindcache = mc;
219
- const listener = () => {
220
- if (mc.isRemoteUpdate()) {
221
- console.log("\u2601\uFE0F CloudAdapter: Skipping remote update");
222
- return;
209
+ mc.doc.on("update", (update, origin) => {
210
+ if (origin !== this && this.ws && this.ws.readyState === WebSocket.OPEN) {
211
+ const encoder = encoding.createEncoder();
212
+ syncProtocol.writeUpdate(encoder, update);
213
+ this.sendBinary(encoding.toUint8Array(encoder));
223
214
  }
224
- console.log("\u2601\uFE0F CloudAdapter: Local change detected, syncing...");
225
- this.syncLocalChanges();
226
- };
227
- mc.subscribeToAll(listener);
228
- this.unsubscribe = () => mc.unsubscribeFromAll(listener);
215
+ });
229
216
  console.log("\u2601\uFE0F CloudAdapter: Attached to MindCache instance");
230
217
  }
231
- /**
232
- * Detach from the MindCache instance
233
- */
234
218
  detach() {
235
219
  if (this.unsubscribe) {
236
220
  this.unsubscribe();
@@ -238,15 +222,6 @@ var init_CloudAdapter = __esm({
238
222
  }
239
223
  this.mindcache = null;
240
224
  }
241
- /**
242
- * Fetch a short-lived WebSocket token from the API using the API key.
243
- * This keeps the API key secure by only using it for a single HTTPS request,
244
- * then using the short-lived token for the WebSocket connection.
245
- *
246
- * Supports two key formats:
247
- * - API keys: mc_live_xxx or mc_test_xxx → Bearer token
248
- * - Delegate keys: del_xxx:sec_xxx → ApiKey format
249
- */
250
225
  async fetchTokenWithApiKey() {
251
226
  if (!this.config.apiKey) {
252
227
  throw new Error("API key is required to fetch token");
@@ -272,9 +247,6 @@ var init_CloudAdapter = __esm({
272
247
  const data = await response.json();
273
248
  return data.token;
274
249
  }
275
- /**
276
- * Connect to the cloud service
277
- */
278
250
  async connect() {
279
251
  if (this._state === "connecting" || this._state === "connected") {
280
252
  return;
@@ -296,6 +268,7 @@ var init_CloudAdapter = __esm({
296
268
  throw new Error("MindCache Cloud: No authentication method available. Provide apiKey or tokenProvider.");
297
269
  }
298
270
  this.ws = new WebSocket(url);
271
+ this.ws.binaryType = "arraybuffer";
299
272
  this.setupWebSocket();
300
273
  } catch (error) {
301
274
  this._state = "error";
@@ -303,9 +276,6 @@ var init_CloudAdapter = __esm({
303
276
  this.scheduleReconnect();
304
277
  }
305
278
  }
306
- /**
307
- * Disconnect from the cloud service
308
- */
309
279
  disconnect() {
310
280
  if (this.reconnectTimeout) {
311
281
  clearTimeout(this.reconnectTimeout);
@@ -318,28 +288,12 @@ var init_CloudAdapter = __esm({
318
288
  this._state = "disconnected";
319
289
  this.emit("disconnected");
320
290
  }
321
- /**
322
- * Push an operation to the cloud
323
- */
324
- push(op) {
325
- if (this.ws?.readyState === WebSocket.OPEN) {
326
- this.ws.send(JSON.stringify(op));
327
- } else {
328
- this.queue.push(op);
329
- }
330
- }
331
- /**
332
- * Add event listener
333
- */
334
291
  on(event, listener) {
335
292
  if (!this.listeners[event]) {
336
293
  this.listeners[event] = [];
337
294
  }
338
295
  this.listeners[event].push(listener);
339
296
  }
340
- /**
341
- * Remove event listener
342
- */
343
297
  off(event, listener) {
344
298
  if (this.listeners[event]) {
345
299
  this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
@@ -355,14 +309,36 @@ var init_CloudAdapter = __esm({
355
309
  return;
356
310
  }
357
311
  this.ws.onopen = () => {
312
+ if (this.mindcache) {
313
+ const encoder = encoding.createEncoder();
314
+ syncProtocol.writeSyncStep1(encoder, this.mindcache.doc);
315
+ this.sendBinary(encoding.toUint8Array(encoder));
316
+ }
358
317
  };
359
318
  this.ws.onmessage = (event) => {
360
319
  try {
361
- const msg = JSON.parse(event.data);
362
- this.handleMessage(msg);
320
+ if (typeof event.data === "string") {
321
+ const msg = JSON.parse(event.data);
322
+ if (msg.type === "auth_success") {
323
+ this._state = "connected";
324
+ this.reconnectAttempts = 0;
325
+ this.emit("connected");
326
+ } else if (msg.type === "auth_error" || msg.type === "error") {
327
+ this._state = "error";
328
+ this.emit("error", new Error(msg.error));
329
+ }
330
+ } else {
331
+ const encoder = encoding.createEncoder();
332
+ const decoder = decoding.createDecoder(new Uint8Array(event.data));
333
+ if (this.mindcache) {
334
+ syncProtocol.readSyncMessage(decoder, encoder, this.mindcache.doc, this);
335
+ if (encoding.length(encoder) > 0) {
336
+ this.sendBinary(encoding.toUint8Array(encoder));
337
+ }
338
+ }
339
+ }
363
340
  } catch (error) {
364
- console.error("MindCache Cloud: Failed to parse message:", error);
365
- console.error("Raw message:", typeof event.data === "string" ? event.data.slice(0, 200) : event.data);
341
+ console.error("MindCache Cloud: Failed to handle message:", error);
366
342
  }
367
343
  };
368
344
  this.ws.onclose = () => {
@@ -372,73 +348,12 @@ var init_CloudAdapter = __esm({
372
348
  };
373
349
  this.ws.onerror = () => {
374
350
  this._state = "error";
375
- const url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
376
- console.error(`MindCache Cloud: WebSocket error connecting to ${url}`);
377
- console.error("Check that the instance ID and API key are correct, and that the server is reachable.");
378
- this.emit("error", new Error(`WebSocket connection failed to ${url}`));
351
+ this.emit("error", new Error("WebSocket connection failed"));
379
352
  };
380
353
  }
381
- handleMessage(msg) {
382
- switch (msg.type) {
383
- case "auth_success":
384
- this._state = "connected";
385
- this.reconnectAttempts = 0;
386
- this.emit("connected");
387
- this.flushQueue();
388
- break;
389
- case "auth_error":
390
- this._state = "error";
391
- this.emit("error", new Error(msg.error));
392
- this.disconnect();
393
- break;
394
- case "sync":
395
- if (this.mindcache && msg.data) {
396
- Object.entries(msg.data).forEach(([key, entry]) => {
397
- const { value, attributes } = entry;
398
- this.mindcache._setFromRemote(key, value, attributes);
399
- });
400
- this.emit("synced");
401
- }
402
- break;
403
- case "set":
404
- if (this.mindcache) {
405
- this.mindcache._setFromRemote(msg.key, msg.value, msg.attributes);
406
- }
407
- break;
408
- case "key_updated":
409
- if (this.mindcache) {
410
- this.mindcache._setFromRemote(msg.key, msg.value, msg.attributes);
411
- }
412
- break;
413
- case "delete":
414
- if (this.mindcache) {
415
- this.mindcache._deleteFromRemote(msg.key);
416
- }
417
- break;
418
- case "key_deleted":
419
- if (this.mindcache) {
420
- this.mindcache._deleteFromRemote(msg.key);
421
- }
422
- break;
423
- case "clear":
424
- if (this.mindcache) {
425
- this.mindcache._clearFromRemote();
426
- }
427
- break;
428
- case "cleared":
429
- if (this.mindcache) {
430
- this.mindcache._clearFromRemote();
431
- }
432
- break;
433
- case "error":
434
- this.emit("error", new Error(msg.error));
435
- break;
436
- }
437
- }
438
- flushQueue() {
439
- while (this.queue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
440
- const op = this.queue.shift();
441
- this.ws.send(JSON.stringify(op));
354
+ sendBinary(data) {
355
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
356
+ this.ws.send(data);
442
357
  }
443
358
  }
444
359
  scheduleReconnect() {
@@ -455,23 +370,6 @@ var init_CloudAdapter = __esm({
455
370
  this.connect();
456
371
  }, delay);
457
372
  }
458
- syncLocalChanges() {
459
- if (!this.mindcache) {
460
- return;
461
- }
462
- const entries = this.mindcache.serialize();
463
- console.log("\u2601\uFE0F CloudAdapter: Syncing local changes:", Object.keys(entries));
464
- Object.entries(entries).forEach(([key, entry]) => {
465
- console.log("\u2601\uFE0F CloudAdapter: Pushing key:", key, "=", entry.value);
466
- this.push({
467
- type: "set",
468
- key,
469
- value: entry.value,
470
- attributes: entry.attributes,
471
- timestamp: Date.now()
472
- });
473
- });
474
- }
475
373
  };
476
374
  }
477
375
  });
@@ -493,16 +391,18 @@ var DEFAULT_KEY_ATTRIBUTES = {
493
391
 
494
392
  // src/core/MindCache.ts
495
393
  var MindCache = class {
496
- stm = {};
394
+ // Public doc for adapter access
395
+ doc;
396
+ rootMap;
397
+ // Key -> EntryMap({value, attributes})
398
+ // Cache listeners
497
399
  listeners = {};
498
400
  globalListeners = [];
401
+ // Metadata
402
+ version = "3.1.0";
499
403
  // Internal flag to prevent sync loops when receiving remote updates
404
+ // (Less critical with Yjs but kept for API compat)
500
405
  _isRemoteUpdate = false;
501
- /**
502
- * Normalize system tags: migrate old tags to new ones
503
- * - 'prompt' → 'SystemPrompt'
504
- * - 'readonly' → remove 'LLMWrite' (or add if not readonly)
505
- */
506
406
  normalizeSystemTags(tags) {
507
407
  const normalized = [];
508
408
  let hasSystemPrompt = false;
@@ -539,24 +439,6 @@ var MindCache = class {
539
439
  }
540
440
  return normalized;
541
441
  }
542
- /**
543
- * Check if key should be visible in system prompt
544
- */
545
- hasSystemPrompt(tags) {
546
- return tags.includes("SystemPrompt") || tags.includes("prompt");
547
- }
548
- /**
549
- * Check if key can be read by LLM (has LLMRead or SystemPrompt)
550
- */
551
- hasLLMRead(tags) {
552
- return tags.includes("LLMRead") || tags.includes("SystemPrompt") || tags.includes("prompt");
553
- }
554
- /**
555
- * Check if key can be written by LLM (has LLMWrite and not readonly)
556
- */
557
- hasLLMWrite(tags) {
558
- return tags.includes("LLMWrite") && !tags.includes("readonly");
559
- }
560
442
  // Cloud sync state
561
443
  _cloudAdapter = null;
562
444
  _connectionState = "disconnected";
@@ -566,25 +448,57 @@ var MindCache = class {
566
448
  // Access level for system operations
567
449
  _accessLevel = "user";
568
450
  _initPromise = null;
451
+ // Y-IndexedDB provider
452
+ _idbProvider = null;
453
+ // Undo Managers Cache
454
+ _undoManagers = /* @__PURE__ */ new Map();
455
+ // Global Undo Manager (watches entire rootMap)
456
+ _globalUndoManager = null;
457
+ // History tracking
458
+ _history = [];
459
+ _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
460
+ _historyEnabled = false;
569
461
  constructor(options) {
462
+ this.doc = new Y.Doc();
463
+ this.rootMap = this.doc.getMap("mindcache");
464
+ this.rootMap.observe((event) => {
465
+ event.keysChanged.forEach((key) => {
466
+ const entryMap = this.rootMap.get(key);
467
+ if (entryMap) {
468
+ const value = entryMap.get("value");
469
+ if (this.listeners[key]) {
470
+ this.listeners[key].forEach((l) => l(value));
471
+ }
472
+ } else {
473
+ if (this.listeners[key]) {
474
+ this.listeners[key].forEach((l) => l(void 0));
475
+ }
476
+ }
477
+ });
478
+ });
479
+ this.rootMap.observeDeep((_events) => {
480
+ this.notifyGlobalListeners();
481
+ });
482
+ this.initGlobalUndoManager();
570
483
  if (options?.accessLevel) {
571
484
  this._accessLevel = options.accessLevel;
572
485
  }
573
- if (options?.cloud && options?.indexedDB) {
574
- throw new Error(
575
- "MindCache: Cannot use both cloud and indexedDB together. Choose one persistence method to avoid data conflicts. Use cloud for real-time sync, or indexedDB for local-only persistence."
576
- );
577
- }
578
486
  const initPromises = [];
579
487
  if (options?.cloud) {
580
488
  this._cloudConfig = options.cloud;
581
489
  this._isLoaded = false;
582
490
  this._connectionState = "disconnected";
583
491
  initPromises.push(this._initCloud());
492
+ if (typeof window !== "undefined") {
493
+ const dbName = `mindcache_cloud_${options.cloud.instanceId}`;
494
+ initPromises.push(this._initYIndexedDB(dbName));
495
+ }
496
+ this.enableHistory(options.history);
584
497
  }
585
- if (options?.indexedDB) {
498
+ if (options?.indexedDB && !options?.cloud) {
586
499
  this._isLoaded = false;
587
- initPromises.push(this._initIndexedDB(options.indexedDB));
500
+ initPromises.push(this._initYIndexedDB(options.indexedDB.dbName || "mindcache_yjs_db"));
501
+ this.enableHistory(options.history);
588
502
  }
589
503
  if (initPromises.length > 0) {
590
504
  this._initPromise = Promise.all(initPromises).then(() => {
@@ -594,15 +508,147 @@ var MindCache = class {
594
508
  });
595
509
  }
596
510
  }
511
+ // Helper: Get or Create UndoManager for a key
512
+ getUndoManager(key) {
513
+ const entryMap = this.rootMap.get(key);
514
+ if (!entryMap) {
515
+ return void 0;
516
+ }
517
+ if (!this._undoManagers.has(key)) {
518
+ const um = new Y.UndoManager(entryMap, {
519
+ captureTimeout: 500
520
+ });
521
+ this._undoManagers.set(key, um);
522
+ }
523
+ return this._undoManagers.get(key);
524
+ }
597
525
  /**
598
- * Get the current access level
526
+ * Undo changes for a specific key
599
527
  */
600
- get accessLevel() {
601
- return this._accessLevel;
528
+ undo(key) {
529
+ const um = this.getUndoManager(key);
530
+ if (um) {
531
+ um.undo();
532
+ }
533
+ }
534
+ /**
535
+ * Redo changes for a specific key
536
+ */
537
+ redo(key) {
538
+ const um = this.getUndoManager(key);
539
+ if (um) {
540
+ um.redo();
541
+ }
542
+ }
543
+ getHistory(key) {
544
+ const um = this.getUndoManager(key);
545
+ if (!um) {
546
+ return [];
547
+ }
548
+ return um.undoStack;
549
+ }
550
+ // Initialize global undo manager (watches entire rootMap)
551
+ initGlobalUndoManager() {
552
+ if (!this._globalUndoManager) {
553
+ this._globalUndoManager = new Y.UndoManager(this.rootMap, {
554
+ captureTimeout: 500
555
+ });
556
+ }
557
+ }
558
+ /**
559
+ * Undo all recent local changes (across all keys)
560
+ * Only undoes YOUR changes, not changes from other users in cloud mode
561
+ */
562
+ undoAll() {
563
+ this.initGlobalUndoManager();
564
+ this._globalUndoManager?.undo();
565
+ }
566
+ /**
567
+ * Redo previously undone local changes
568
+ */
569
+ redoAll() {
570
+ this.initGlobalUndoManager();
571
+ this._globalUndoManager?.redo();
572
+ }
573
+ /**
574
+ * Check if there are changes to undo globally
575
+ */
576
+ canUndoAll() {
577
+ this.initGlobalUndoManager();
578
+ return (this._globalUndoManager?.undoStack.length ?? 0) > 0;
579
+ }
580
+ /**
581
+ * Check if there are changes to redo globally
582
+ */
583
+ canRedoAll() {
584
+ this.initGlobalUndoManager();
585
+ return (this._globalUndoManager?.redoStack.length ?? 0) > 0;
586
+ }
587
+ // Enable history tracking (called for IndexedDB and Cloud modes)
588
+ enableHistory(options) {
589
+ if (this._historyEnabled) {
590
+ return;
591
+ }
592
+ this._historyEnabled = true;
593
+ if (options) {
594
+ this._historyOptions = { ...this._historyOptions, ...options };
595
+ }
596
+ this.rootMap.observeDeep((events) => {
597
+ const keysAffected = /* @__PURE__ */ new Set();
598
+ events.forEach((event) => {
599
+ if (event.target === this.rootMap) {
600
+ const mapEvent = event;
601
+ mapEvent.keysChanged.forEach((key) => keysAffected.add(key));
602
+ } else if (event.target.parent === this.rootMap) {
603
+ for (const [key, val] of this.rootMap) {
604
+ if (val === event.target) {
605
+ keysAffected.add(key);
606
+ break;
607
+ }
608
+ }
609
+ }
610
+ });
611
+ if (keysAffected.size > 0) {
612
+ const entry = {
613
+ id: this.generateId(),
614
+ timestamp: Date.now(),
615
+ keysAffected: Array.from(keysAffected)
616
+ };
617
+ this._history.push(entry);
618
+ const max = this._historyOptions.maxEntries || 100;
619
+ if (this._history.length > max) {
620
+ this._history = this._history.slice(-max);
621
+ }
622
+ }
623
+ });
624
+ }
625
+ generateId() {
626
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
627
+ }
628
+ /**
629
+ * Get global history of all changes (available in IndexedDB and Cloud modes)
630
+ */
631
+ getGlobalHistory() {
632
+ return [...this._history];
633
+ }
634
+ /**
635
+ * Check if history tracking is enabled
636
+ */
637
+ get historyEnabled() {
638
+ return this._historyEnabled;
602
639
  }
603
640
  /**
604
- * Check if this instance has system-level access
641
+ * Restore to a specific version (time travel)
642
+ * Note: Full implementation requires storing update binaries, which is not yet implemented.
643
+ * @returns false - not yet fully implemented
605
644
  */
645
+ restoreToVersion(_versionId) {
646
+ console.warn("restoreToVersion: Full implementation requires storing update binaries. Not yet implemented.");
647
+ return false;
648
+ }
649
+ get accessLevel() {
650
+ return this._accessLevel;
651
+ }
606
652
  get hasSystemAccess() {
607
653
  return this._accessLevel === "system";
608
654
  }
@@ -610,110 +656,92 @@ var MindCache = class {
610
656
  if (!this._cloudConfig) {
611
657
  return;
612
658
  }
613
- try {
614
- const CloudAdapter2 = await this._getCloudAdapterClass();
615
- if (!this._cloudConfig.baseUrl) {
616
- throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
659
+ const CloudAdapter2 = await this._getCloudAdapterClass();
660
+ if (!this._cloudConfig.baseUrl) ;
661
+ const baseUrl = (this._cloudConfig.baseUrl || "https://api.mindcache.io").replace("https://", "wss://").replace("http://", "ws://");
662
+ const adapter = new CloudAdapter2({
663
+ instanceId: this._cloudConfig.instanceId,
664
+ projectId: this._cloudConfig.projectId || "default",
665
+ baseUrl,
666
+ apiKey: this._cloudConfig.apiKey
667
+ });
668
+ if (this._cloudConfig.tokenEndpoint) {
669
+ const tokenEndpoint = this._cloudConfig.tokenEndpoint;
670
+ const instanceId = this._cloudConfig.instanceId;
671
+ let resolvedBaseUrl;
672
+ if (tokenEndpoint.startsWith("http://") || tokenEndpoint.startsWith("https://")) {
673
+ resolvedBaseUrl = tokenEndpoint;
674
+ } else if (typeof window !== "undefined" && window.location?.origin) {
675
+ resolvedBaseUrl = `${window.location.origin}${tokenEndpoint.startsWith("/") ? "" : "/"}${tokenEndpoint}`;
676
+ } else {
677
+ resolvedBaseUrl = tokenEndpoint;
617
678
  }
618
- const baseUrl = this._cloudConfig.baseUrl.replace("https://", "wss://").replace("http://", "ws://");
619
- const adapter = new CloudAdapter2({
620
- instanceId: this._cloudConfig.instanceId,
621
- projectId: this._cloudConfig.projectId || "default",
622
- baseUrl,
623
- apiKey: this._cloudConfig.apiKey
624
- });
625
- if (this._cloudConfig.tokenEndpoint) {
626
- const tokenEndpoint = this._cloudConfig.tokenEndpoint;
627
- const instanceId = this._cloudConfig.instanceId;
628
- let resolvedBaseUrl;
629
- if (tokenEndpoint.startsWith("http://") || tokenEndpoint.startsWith("https://")) {
630
- resolvedBaseUrl = tokenEndpoint;
631
- } else if (typeof window !== "undefined" && window.location?.origin) {
632
- resolvedBaseUrl = `${window.location.origin}${tokenEndpoint.startsWith("/") ? "" : "/"}${tokenEndpoint}`;
633
- } else {
634
- console.warn("MindCache: Cannot resolve tokenEndpoint to absolute URL - window.location not available");
635
- resolvedBaseUrl = tokenEndpoint;
679
+ adapter.setTokenProvider(async () => {
680
+ const url = resolvedBaseUrl.includes("?") ? `${resolvedBaseUrl}&instanceId=${instanceId}` : `${resolvedBaseUrl}?instanceId=${instanceId}`;
681
+ const response = await fetch(url);
682
+ if (!response.ok) {
683
+ throw new Error("Failed to get token");
636
684
  }
637
- adapter.setTokenProvider(async () => {
638
- const url = resolvedBaseUrl.includes("?") ? `${resolvedBaseUrl}&instanceId=${instanceId}` : `${resolvedBaseUrl}?instanceId=${instanceId}`;
639
- const response = await fetch(url);
640
- if (!response.ok) {
641
- const error = await response.json().catch(() => ({ error: "Failed to get token" }));
642
- throw new Error(error.error || "Failed to get token");
643
- }
644
- const data = await response.json();
645
- return data.token;
646
- });
647
- }
648
- adapter.on("connected", () => {
649
- this._connectionState = "connected";
650
- this.notifyGlobalListeners();
651
- });
652
- adapter.on("disconnected", () => {
653
- this._connectionState = "disconnected";
654
- this.notifyGlobalListeners();
655
- });
656
- adapter.on("error", () => {
657
- this._connectionState = "error";
658
- this.notifyGlobalListeners();
659
- });
660
- adapter.on("synced", () => {
661
- this._isLoaded = true;
662
- this.notifyGlobalListeners();
685
+ const data = await response.json();
686
+ return data.token;
663
687
  });
664
- adapter.attach(this);
665
- this._cloudAdapter = adapter;
666
- this._connectionState = "connecting";
667
- adapter.connect();
668
- } catch (error) {
669
- console.error("MindCache: Failed to initialize cloud connection:", error);
688
+ }
689
+ adapter.on("connected", () => {
690
+ this._connectionState = "connected";
691
+ this.notifyGlobalListeners();
692
+ });
693
+ adapter.on("disconnected", () => {
694
+ this._connectionState = "disconnected";
695
+ this.notifyGlobalListeners();
696
+ });
697
+ adapter.on("error", () => {
670
698
  this._connectionState = "error";
699
+ this.notifyGlobalListeners();
700
+ });
701
+ adapter.on("synced", () => {
671
702
  this._isLoaded = true;
672
- }
703
+ this.notifyGlobalListeners();
704
+ });
705
+ adapter.attach(this);
706
+ this._cloudAdapter = adapter;
707
+ this._connectionState = "connecting";
708
+ adapter.connect();
673
709
  }
674
- async _initIndexedDB(config) {
675
- try {
676
- const IndexedDBAdapter2 = await this._getIndexedDBAdapterClass();
677
- const adapter = new IndexedDBAdapter2(config);
678
- await adapter.attach(this);
679
- } catch (error) {
680
- console.error("MindCache: Failed to initialize IndexedDB:", error);
710
+ async _initYIndexedDB(dbName) {
711
+ if (typeof window === "undefined") {
712
+ return;
681
713
  }
714
+ this._idbProvider = new IndexeddbPersistence(dbName, this.doc);
715
+ return new Promise((resolve) => {
716
+ if (!this._idbProvider) {
717
+ return resolve();
718
+ }
719
+ this._idbProvider.on("synced", () => {
720
+ this._isLoaded = true;
721
+ resolve();
722
+ });
723
+ });
724
+ }
725
+ // Legacy IndexedDB method stub
726
+ async _initIndexedDB(_config) {
682
727
  }
683
728
  async _getIndexedDBAdapterClass() {
684
729
  const { IndexedDBAdapter: IndexedDBAdapter2 } = await Promise.resolve().then(() => (init_IndexedDBAdapter(), IndexedDBAdapter_exports));
685
730
  return IndexedDBAdapter2;
686
731
  }
687
- /**
688
- * Get the current cloud connection state
689
- */
690
732
  get connectionState() {
691
733
  return this._connectionState;
692
734
  }
693
- /**
694
- * Check if data is loaded (true for local, true after sync for cloud)
695
- */
696
735
  get isLoaded() {
697
736
  return this._isLoaded;
698
737
  }
699
- /**
700
- * Protected method to load CloudAdapter class.
701
- * Can be overridden/mocked for testing.
702
- */
703
738
  async _getCloudAdapterClass() {
704
739
  const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
705
740
  return CloudAdapter2;
706
741
  }
707
- /**
708
- * Check if this instance is connected to cloud
709
- */
710
742
  get isCloud() {
711
743
  return this._cloudConfig !== null;
712
744
  }
713
- /**
714
- * Wait for initial sync to complete (or resolve immediately if already synced/local).
715
- * Useful for scripts or linear execution flows.
716
- */
717
745
  async waitForSync() {
718
746
  if (this._isLoaded) {
719
747
  return;
@@ -725,20 +753,17 @@ var MindCache = class {
725
753
  return;
726
754
  }
727
755
  return new Promise((resolve) => {
728
- if (!this._cloudAdapter) {
729
- resolve();
730
- return;
756
+ if (this._isLoaded) {
757
+ return resolve();
731
758
  }
732
- const handler = () => {
733
- this._cloudAdapter?.off("synced", handler);
734
- resolve();
735
- };
736
- this._cloudAdapter.on("synced", handler);
759
+ const interval = setInterval(() => {
760
+ if (this._isLoaded) {
761
+ clearInterval(interval);
762
+ resolve();
763
+ }
764
+ }, 100);
737
765
  });
738
766
  }
739
- /**
740
- * Disconnect from cloud (if connected)
741
- */
742
767
  disconnect() {
743
768
  if (this._cloudAdapter) {
744
769
  this._cloudAdapter.disconnect();
@@ -746,8 +771,49 @@ var MindCache = class {
746
771
  this._cloudAdapter = null;
747
772
  this._connectionState = "disconnected";
748
773
  }
774
+ if (this._idbProvider) {
775
+ this._idbProvider.destroy();
776
+ this._idbProvider = null;
777
+ }
778
+ }
779
+ // Legacy bridge
780
+ isRemoteUpdate() {
781
+ return false;
782
+ }
783
+ // Serialize state
784
+ serialize() {
785
+ const json = {};
786
+ for (const [key, val] of this.rootMap) {
787
+ const entryMap = val;
788
+ const attrs = entryMap.get("attributes");
789
+ let value = entryMap.get("value");
790
+ if (attrs?.type === "document" && value instanceof Y.Text) {
791
+ value = value.toString();
792
+ }
793
+ json[key] = {
794
+ value,
795
+ attributes: attrs
796
+ };
797
+ }
798
+ return json;
799
+ }
800
+ // Deserialize state (for IndexedDBAdapter compatibility)
801
+ deserialize(data) {
802
+ this.doc.transact(() => {
803
+ for (const [key, entry] of Object.entries(data)) {
804
+ if (key.startsWith("$")) {
805
+ continue;
806
+ }
807
+ let entryMap = this.rootMap.get(key);
808
+ if (!entryMap) {
809
+ entryMap = new Y.Map();
810
+ this.rootMap.set(key, entryMap);
811
+ }
812
+ entryMap.set("value", entry.value);
813
+ entryMap.set("attributes", entry.attributes);
814
+ }
815
+ });
749
816
  }
750
- // Helper method to encode file to base64
751
817
  encodeFileToBase64(file) {
752
818
  return new Promise((resolve, reject) => {
753
819
  if (typeof FileReader !== "undefined") {
@@ -764,11 +830,9 @@ var MindCache = class {
764
830
  }
765
831
  });
766
832
  }
767
- // Helper method to create data URL from base64 and content type
768
833
  createDataUrl(base64Data, contentType) {
769
834
  return `data:${contentType};base64,${base64Data}`;
770
835
  }
771
- // Helper method to validate content type for different STM types
772
836
  validateContentType(type, contentType) {
773
837
  if (type === "text" || type === "json") {
774
838
  return true;
@@ -784,11 +848,17 @@ var MindCache = class {
784
848
  }
785
849
  return false;
786
850
  }
787
- /** @deprecated Use get_value instead */
788
- get(key) {
789
- return this.get_value(key);
851
+ // InjectSTM replacement
852
+ injectSTM(template, _processingStack) {
853
+ return template.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
854
+ const val = this.get_value(key.trim(), _processingStack);
855
+ return val !== void 0 ? String(val) : `{{${key}}}`;
856
+ });
857
+ }
858
+ // Public API Methods
859
+ getAll() {
860
+ return this.serialize();
790
861
  }
791
- // Get a value from the STM with template processing if enabled
792
862
  get_value(key, _processingStack) {
793
863
  if (key === "$date") {
794
864
  const today = /* @__PURE__ */ new Date();
@@ -798,32 +868,37 @@ var MindCache = class {
798
868
  const now = /* @__PURE__ */ new Date();
799
869
  return now.toTimeString().split(" ")[0];
800
870
  }
801
- const entry = this.stm[key];
802
- if (!entry) {
871
+ if (key === "$version") {
872
+ return this.version;
873
+ }
874
+ const entryMap = this.rootMap.get(key);
875
+ if (!entryMap) {
803
876
  return void 0;
804
877
  }
805
- if (entry.attributes.systemTags?.includes("ApplyTemplate") || entry.attributes.systemTags?.includes("template") || entry.attributes.template) {
806
- const processingStack = _processingStack || /* @__PURE__ */ new Set();
807
- if (processingStack.has(key)) {
808
- return entry.value;
878
+ const attributes = entryMap.get("attributes");
879
+ const value = entryMap.get("value");
880
+ if (attributes?.type === "document" && value instanceof Y.Text) {
881
+ return value.toString();
882
+ }
883
+ if (_processingStack && _processingStack.has(key)) {
884
+ return `{{${key}}}`;
885
+ }
886
+ if (attributes?.systemTags?.includes("ApplyTemplate") || attributes?.systemTags?.includes("template") || attributes?.template) {
887
+ if (typeof value === "string") {
888
+ const stack = _processingStack || /* @__PURE__ */ new Set();
889
+ stack.add(key);
890
+ return this.injectSTM(value, stack);
809
891
  }
810
- processingStack.add(key);
811
- const result = this.injectSTM(entry.value, processingStack);
812
- processingStack.delete(key);
813
- return result;
814
892
  }
815
- return entry.value;
893
+ return value;
816
894
  }
817
- // Get attributes for a key
818
895
  get_attributes(key) {
819
- if (key === "$date" || key === "$time") {
896
+ if (key === "$date" || key === "$time" || key === "$version") {
820
897
  return {
821
898
  type: "text",
822
899
  contentTags: [],
823
900
  systemTags: ["prompt", "readonly", "protected"],
824
901
  zIndex: 999999,
825
- // System keys appear last
826
- // Legacy attributes
827
902
  readonly: true,
828
903
  visible: true,
829
904
  hardcoded: true,
@@ -831,1270 +906,486 @@ var MindCache = class {
831
906
  tags: []
832
907
  };
833
908
  }
834
- const entry = this.stm[key];
835
- return entry ? entry.attributes : void 0;
909
+ const entryMap = this.rootMap.get(key);
910
+ return entryMap ? entryMap.get("attributes") : void 0;
836
911
  }
837
- // Set a value in the STM with default attributes
838
912
  set_value(key, value, attributes) {
839
- if (key === "$date" || key === "$time") {
913
+ if (key === "$date" || key === "$time" || key === "$version") {
840
914
  return;
841
915
  }
842
- const existingEntry = this.stm[key];
843
- const wasHardcoded = existingEntry?.attributes.hardcoded || existingEntry?.attributes.systemTags?.includes("protected");
844
- const baseAttributes = existingEntry ? {
845
- ...existingEntry.attributes,
846
- contentTags: [...existingEntry.attributes.contentTags || []],
847
- systemTags: [...existingEntry.attributes.systemTags || []],
848
- tags: [...existingEntry.attributes.tags || []],
849
- zIndex: existingEntry.attributes.zIndex ?? 0
850
- } : {
851
- ...DEFAULT_KEY_ATTRIBUTES,
852
- contentTags: [],
853
- // Fresh array
854
- systemTags: ["SystemPrompt", "LLMWrite"],
855
- // Fresh array with default
856
- tags: [],
857
- // Fresh array
858
- zIndex: 0
859
- };
860
- const finalAttributes = attributes ? { ...baseAttributes, ...attributes } : baseAttributes;
861
- let systemTags = this.normalizeSystemTags(finalAttributes.systemTags || []);
862
- if (attributes) {
863
- if ("readonly" in attributes) {
864
- if (attributes.readonly) {
865
- systemTags = systemTags.filter((t) => t !== "LLMWrite");
866
- if (!systemTags.includes("readonly")) {
867
- systemTags.push("readonly");
868
- }
869
- } else if (!wasHardcoded) {
870
- if (!systemTags.includes("LLMWrite")) {
871
- systemTags.push("LLMWrite");
872
- }
873
- systemTags = systemTags.filter((t) => t !== "readonly");
874
- }
875
- }
876
- if ("visible" in attributes) {
877
- if (attributes.visible) {
878
- if (!systemTags.includes("SystemPrompt")) {
879
- systemTags.push("SystemPrompt");
880
- }
881
- systemTags = systemTags.filter((t) => t !== "prompt");
882
- } else {
883
- systemTags = systemTags.filter((t) => t !== "SystemPrompt" && t !== "prompt");
884
- }
885
- }
886
- if ("systemTags" in attributes && Array.isArray(attributes.systemTags)) {
887
- systemTags = this.normalizeSystemTags(attributes.systemTags);
888
- }
889
- if ("hardcoded" in attributes) {
890
- if (attributes.hardcoded && !systemTags.includes("protected")) {
891
- systemTags.push("protected");
892
- } else if (!attributes.hardcoded && !wasHardcoded) {
893
- systemTags = systemTags.filter((t) => t !== "protected");
894
- }
895
- if (wasHardcoded && !systemTags.includes("protected")) {
896
- systemTags.push("protected");
897
- }
898
- } else if (wasHardcoded) {
899
- if (!systemTags.includes("protected")) {
900
- systemTags.push("protected");
901
- }
902
- }
903
- if ("template" in attributes) {
904
- if (attributes.template && !wasHardcoded && !systemTags.includes("ApplyTemplate") && !systemTags.includes("template")) {
905
- systemTags.push("ApplyTemplate");
906
- } else if (!attributes.template || wasHardcoded) {
907
- systemTags = systemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
916
+ const existingEntry = this.rootMap.get(key);
917
+ if (existingEntry) {
918
+ const existingAttrs = existingEntry.get("attributes");
919
+ if (existingAttrs?.type === "document") {
920
+ if (typeof value === "string") {
921
+ this.replace_document_text(key, value);
908
922
  }
923
+ return;
909
924
  }
910
- } else if (wasHardcoded) {
911
- if (!systemTags.includes("protected")) {
912
- systemTags.push("protected");
913
- }
914
- systemTags = systemTags.filter((t) => t !== "LLMWrite");
915
- if (!systemTags.includes("readonly")) {
916
- systemTags.push("readonly");
917
- }
918
- systemTags = systemTags.filter((t) => t !== "template");
919
925
  }
920
- if (wasHardcoded && !systemTags.includes("protected")) {
921
- systemTags.push("protected");
926
+ let entryMap = this.rootMap.get(key);
927
+ const isNewEntry = !entryMap;
928
+ if (isNewEntry) {
929
+ entryMap = new Y.Map();
930
+ this.rootMap.set(key, entryMap);
922
931
  }
923
- if (systemTags.includes("protected")) {
924
- systemTags = systemTags.filter((t) => t !== "LLMWrite");
925
- if (!systemTags.includes("readonly")) {
926
- systemTags.push("readonly");
932
+ this.getUndoManager(key);
933
+ this.doc.transact(() => {
934
+ const oldAttributes = isNewEntry ? {
935
+ ...DEFAULT_KEY_ATTRIBUTES,
936
+ contentTags: [],
937
+ systemTags: ["SystemPrompt", "LLMWrite"],
938
+ tags: [],
939
+ zIndex: 0
940
+ } : entryMap.get("attributes");
941
+ const finalAttributes = attributes ? { ...oldAttributes, ...attributes } : oldAttributes;
942
+ let normalizedAttributes = { ...finalAttributes };
943
+ if (finalAttributes.systemTags) {
944
+ normalizedAttributes.systemTags = this.normalizeSystemTags(finalAttributes.systemTags);
945
+ }
946
+ if (finalAttributes.template) {
947
+ if (!normalizedAttributes.systemTags.includes("template")) {
948
+ normalizedAttributes.systemTags.push("template");
949
+ }
927
950
  }
928
- systemTags = systemTags.filter((t) => t !== "template");
951
+ entryMap.set("value", value);
952
+ entryMap.set("attributes", normalizedAttributes);
953
+ });
954
+ }
955
+ delete_key(key) {
956
+ if (key === "$date" || key === "$time") {
957
+ return;
929
958
  }
930
- finalAttributes.systemTags = systemTags;
931
- finalAttributes.readonly = systemTags.includes("readonly") || !systemTags.includes("LLMWrite");
932
- finalAttributes.visible = this.hasSystemPrompt(systemTags);
933
- finalAttributes.hardcoded = wasHardcoded || systemTags.includes("protected");
934
- finalAttributes.template = systemTags.includes("ApplyTemplate") || systemTags.includes("template");
935
- if (attributes && "tags" in attributes && attributes.tags) {
936
- finalAttributes.contentTags = [...attributes.tags];
959
+ this.rootMap.delete(key);
960
+ }
961
+ clear() {
962
+ const keys = Array.from(this.rootMap.keys());
963
+ this.doc.transact(() => {
964
+ keys.forEach((k) => this.rootMap.delete(k));
965
+ });
966
+ }
967
+ // File methods
968
+ async set_file(key, file, attributes) {
969
+ const base64 = await this.encodeFileToBase64(file);
970
+ const dataUrl = this.createDataUrl(base64, file.type);
971
+ this.set_value(key, dataUrl, {
972
+ ...attributes,
973
+ type: "file",
974
+ contentType: file.type
975
+ });
976
+ }
977
+ set_image(key, file, attributes) {
978
+ return this.set_file(key, file, {
979
+ ...attributes,
980
+ type: "image"
981
+ // Override to image
982
+ });
983
+ }
984
+ // Document methods for collaborative editing
985
+ /**
986
+ * Create or get a collaborative document key.
987
+ * Uses Y.Text for character-level concurrent editing.
988
+ *
989
+ * Note: This exposes Yjs Y.Text directly for editor bindings (y-quill, y-codemirror, etc.)
990
+ */
991
+ set_document(key, initialText, attributes) {
992
+ if (key === "$date" || key === "$time" || key === "$version") {
993
+ return;
937
994
  }
938
- finalAttributes.tags = [...finalAttributes.contentTags || []];
939
- this.stm[key] = {
940
- value,
941
- attributes: finalAttributes
942
- };
943
- if (this.listeners[key]) {
944
- this.listeners[key].forEach((listener) => listener(value));
995
+ let entryMap = this.rootMap.get(key);
996
+ if (!entryMap) {
997
+ entryMap = new Y.Map();
998
+ this.rootMap.set(key, entryMap);
999
+ const yText = new Y.Text(initialText || "");
1000
+ entryMap.set("value", yText);
1001
+ entryMap.set("attributes", {
1002
+ ...DEFAULT_KEY_ATTRIBUTES,
1003
+ type: "document",
1004
+ contentTags: [],
1005
+ systemTags: ["SystemPrompt", "LLMWrite"],
1006
+ tags: [],
1007
+ zIndex: 0,
1008
+ ...attributes
1009
+ });
945
1010
  }
946
- this.notifyGlobalListeners();
1011
+ this.getUndoManager(key);
947
1012
  }
948
- // Internal method for setting values from remote (cloud sync)
949
- // This doesn't trigger the global listener to prevent sync loops
950
- _setFromRemote(key, value, attributes) {
951
- if (key === "$date" || key === "$time") {
952
- return;
1013
+ /**
1014
+ * Get the Y.Text object for a document key.
1015
+ * Use this to bind to editors (Quill, CodeMirror, Monaco, etc.)
1016
+ *
1017
+ * @returns Y.Text or undefined if key doesn't exist or isn't a document
1018
+ */
1019
+ get_document(key) {
1020
+ const entryMap = this.rootMap.get(key);
1021
+ if (!entryMap) {
1022
+ return void 0;
953
1023
  }
954
- this._isRemoteUpdate = true;
955
- let systemTags = attributes.systemTags || [];
956
- if (!attributes.systemTags || systemTags.length === 0) {
957
- systemTags = [];
958
- if (attributes.visible !== false) {
959
- systemTags.push("prompt");
960
- }
961
- if (attributes.readonly) {
962
- systemTags.push("readonly");
963
- } else {
964
- systemTags.push("LLMWrite");
965
- }
966
- if (attributes.hardcoded) {
967
- systemTags.push("protected");
968
- }
969
- if (attributes.template) {
970
- systemTags.push("ApplyTemplate");
971
- }
1024
+ const attrs = entryMap.get("attributes");
1025
+ if (attrs?.type !== "document") {
1026
+ return void 0;
972
1027
  }
973
- systemTags = this.normalizeSystemTags(systemTags);
974
- const contentTags = attributes.contentTags || attributes.tags || [];
975
- this.stm[key] = {
976
- value,
977
- attributes: {
978
- ...attributes,
979
- contentTags,
980
- systemTags,
981
- zIndex: attributes.zIndex ?? 0,
982
- tags: contentTags,
983
- // Sync legacy attributes FROM normalized systemTags
984
- readonly: systemTags.includes("readonly") || !systemTags.includes("LLMWrite"),
985
- visible: this.hasSystemPrompt(systemTags),
986
- hardcoded: systemTags.includes("protected"),
987
- template: systemTags.includes("ApplyTemplate") || systemTags.includes("template")
988
- }
989
- };
990
- if (this.listeners[key]) {
991
- this.listeners[key].forEach((listener) => listener(value));
1028
+ const value = entryMap.get("value");
1029
+ if (value instanceof Y.Text) {
1030
+ return value;
992
1031
  }
993
- this.notifyGlobalListeners();
994
- this._isRemoteUpdate = false;
1032
+ return void 0;
995
1033
  }
996
- // Check if current update is from remote
997
- isRemoteUpdate() {
998
- return this._isRemoteUpdate;
1034
+ /**
1035
+ * Get plain text content of a document key.
1036
+ * For collaborative editing, use get_document() and bind to an editor.
1037
+ */
1038
+ get_document_text(key) {
1039
+ const yText = this.get_document(key);
1040
+ return yText?.toString();
999
1041
  }
1000
- // Internal method for deleting from remote (cloud sync)
1001
- _deleteFromRemote(key) {
1002
- if (key === "$date" || key === "$time") {
1003
- return;
1004
- }
1005
- this._isRemoteUpdate = true;
1006
- if (key in this.stm) {
1007
- delete this.stm[key];
1008
- if (this.listeners[key]) {
1009
- this.listeners[key].forEach((listener) => listener(void 0));
1010
- }
1011
- this.notifyGlobalListeners();
1042
+ /**
1043
+ * Insert text at a position in a document key.
1044
+ */
1045
+ insert_text(key, index, text) {
1046
+ const yText = this.get_document(key);
1047
+ if (yText) {
1048
+ yText.insert(index, text);
1012
1049
  }
1013
- this._isRemoteUpdate = false;
1014
1050
  }
1015
- // Internal method for clearing from remote (cloud sync)
1016
- _clearFromRemote() {
1017
- this._isRemoteUpdate = true;
1018
- this.stm = {};
1019
- this.notifyGlobalListeners();
1020
- this._isRemoteUpdate = false;
1051
+ /**
1052
+ * Delete text from a document key.
1053
+ */
1054
+ delete_text(key, index, length2) {
1055
+ const yText = this.get_document(key);
1056
+ if (yText) {
1057
+ yText.delete(index, length2);
1058
+ }
1021
1059
  }
1022
- // Set attributes for an existing key
1023
- set_attributes(key, attributes) {
1024
- if (key === "$date" || key === "$time") {
1025
- return false;
1060
+ /**
1061
+ * Replace all text in a document key.
1062
+ * Uses diff-based updates when changes are < diffThreshold (default 80%).
1063
+ * This preserves concurrent edits and provides better undo granularity.
1064
+ *
1065
+ * @param key - The document key
1066
+ * @param newText - The new text content
1067
+ * @param diffThreshold - Percentage (0-1) of change above which full replace is used (default: 0.8)
1068
+ */
1069
+ replace_document_text(key, newText, diffThreshold = 0.8) {
1070
+ const yText = this.get_document(key);
1071
+ if (!yText) {
1072
+ return;
1026
1073
  }
1027
- const entry = this.stm[key];
1028
- if (!entry) {
1029
- return false;
1074
+ const oldText = yText.toString();
1075
+ if (oldText === newText) {
1076
+ return;
1030
1077
  }
1031
- const wasHardcoded = entry.attributes.hardcoded || entry.attributes.systemTags?.includes("protected");
1032
- const { hardcoded: _hardcoded, systemTags: _systemTags, ...allowedAttributes } = attributes;
1033
- if (wasHardcoded) {
1034
- if ("readonly" in allowedAttributes) {
1035
- delete allowedAttributes.readonly;
1036
- }
1037
- if ("template" in allowedAttributes) {
1038
- delete allowedAttributes.template;
1078
+ if (oldText.length === 0) {
1079
+ yText.insert(0, newText);
1080
+ return;
1081
+ }
1082
+ const diffs = diff(oldText, newText);
1083
+ let changedChars = 0;
1084
+ for (const [op, text] of diffs) {
1085
+ if (op !== 0) {
1086
+ changedChars += text.length;
1039
1087
  }
1040
1088
  }
1041
- entry.attributes = { ...entry.attributes, ...allowedAttributes };
1042
- if ("readonly" in attributes || "visible" in attributes || "template" in attributes || "systemTags" in attributes) {
1043
- let newSystemTags = entry.attributes.systemTags || [];
1044
- if ("systemTags" in attributes && Array.isArray(attributes.systemTags)) {
1045
- newSystemTags = this.normalizeSystemTags(attributes.systemTags);
1046
- } else {
1047
- newSystemTags = [];
1048
- if (!entry.attributes.readonly) {
1049
- newSystemTags.push("LLMWrite");
1050
- } else {
1051
- newSystemTags.push("readonly");
1052
- }
1053
- if (entry.attributes.visible) {
1054
- newSystemTags.push("SystemPrompt");
1055
- }
1056
- if (entry.attributes.template) {
1057
- newSystemTags.push("ApplyTemplate");
1058
- }
1059
- if (wasHardcoded || entry.attributes.hardcoded) {
1060
- newSystemTags.push("protected");
1061
- }
1062
- newSystemTags = this.normalizeSystemTags(newSystemTags);
1063
- }
1064
- if (newSystemTags.includes("protected")) {
1065
- newSystemTags = newSystemTags.filter((t) => t !== "LLMWrite");
1066
- if (!newSystemTags.includes("readonly")) {
1067
- newSystemTags.push("readonly");
1068
- }
1069
- newSystemTags = newSystemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
1070
- entry.attributes.readonly = true;
1071
- entry.attributes.template = false;
1072
- }
1073
- entry.attributes.systemTags = newSystemTags;
1074
- entry.attributes.readonly = newSystemTags.includes("readonly") || !newSystemTags.includes("LLMWrite");
1075
- entry.attributes.visible = this.hasSystemPrompt(newSystemTags);
1076
- entry.attributes.template = newSystemTags.includes("ApplyTemplate") || newSystemTags.includes("template");
1077
- } else if (wasHardcoded) {
1078
- let systemTags = this.normalizeSystemTags(entry.attributes.systemTags || []);
1079
- if (!systemTags.includes("protected")) {
1080
- systemTags.push("protected");
1081
- }
1082
- systemTags = systemTags.filter((t) => t !== "LLMWrite");
1083
- if (!systemTags.includes("readonly")) {
1084
- systemTags.push("readonly");
1085
- }
1086
- systemTags = systemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
1087
- entry.attributes.systemTags = systemTags;
1088
- entry.attributes.readonly = true;
1089
- entry.attributes.visible = this.hasSystemPrompt(systemTags);
1090
- entry.attributes.template = false;
1091
- }
1092
- if (wasHardcoded) {
1093
- entry.attributes.hardcoded = true;
1094
- if (!entry.attributes.systemTags?.includes("protected")) {
1095
- entry.attributes.systemTags = [...entry.attributes.systemTags || [], "protected"];
1096
- }
1097
- entry.attributes.readonly = true;
1098
- entry.attributes.template = false;
1099
- }
1100
- if ("contentTags" in attributes) {
1101
- entry.attributes.tags = [...entry.attributes.contentTags || []];
1102
- }
1103
- this.notifyGlobalListeners();
1104
- return true;
1105
- }
1106
- set(key, value) {
1107
- this.set_value(key, value);
1108
- }
1109
- async set_file(key, file, attributes) {
1110
- const base64Data = await this.encodeFileToBase64(file);
1111
- const contentType = file.type;
1112
- const fileAttributes = {
1113
- type: contentType.startsWith("image/") ? "image" : "file",
1114
- contentType,
1115
- ...attributes
1116
- };
1117
- this.set_value(key, base64Data, fileAttributes);
1118
- }
1119
- set_base64(key, base64Data, contentType, type = "file", attributes) {
1120
- if (!this.validateContentType(type, contentType)) {
1121
- throw new Error(`Invalid content type ${contentType} for type ${type}`);
1122
- }
1123
- const fileAttributes = {
1124
- type,
1125
- contentType,
1126
- ...attributes
1127
- };
1128
- this.set_value(key, base64Data, fileAttributes);
1129
- }
1130
- add_image(key, base64Data, contentType = "image/jpeg", attributes) {
1131
- if (!contentType.startsWith("image/")) {
1132
- throw new Error(`Invalid image content type: ${contentType}. Must start with 'image/'`);
1133
- }
1134
- this.set_base64(key, base64Data, contentType, "image", attributes);
1135
- this.set_attributes(key, {
1136
- type: "image",
1137
- contentType
1138
- });
1139
- }
1140
- get_data_url(key) {
1141
- const entry = this.stm[key];
1142
- if (!entry || entry.attributes.type !== "image" && entry.attributes.type !== "file") {
1143
- return void 0;
1144
- }
1145
- if (!entry.attributes.contentType) {
1146
- return void 0;
1147
- }
1148
- return this.createDataUrl(entry.value, entry.attributes.contentType);
1149
- }
1150
- get_base64(key) {
1151
- const entry = this.stm[key];
1152
- if (!entry || entry.attributes.type !== "image" && entry.attributes.type !== "file") {
1153
- return void 0;
1154
- }
1155
- return entry.value;
1156
- }
1157
- has(key) {
1158
- if (key === "$date" || key === "$time") {
1159
- return true;
1160
- }
1161
- return key in this.stm;
1162
- }
1163
- delete(key) {
1164
- if (key === "$date" || key === "$time") {
1165
- return false;
1166
- }
1167
- if (!(key in this.stm)) {
1168
- return false;
1169
- }
1170
- const deleted = delete this.stm[key];
1171
- if (deleted) {
1172
- this.notifyGlobalListeners();
1173
- if (this.listeners[key]) {
1174
- this.listeners[key].forEach((listener) => listener(void 0));
1175
- }
1176
- }
1177
- return deleted;
1178
- }
1179
- clear() {
1180
- this.stm = {};
1181
- this.notifyGlobalListeners();
1182
- }
1183
- /**
1184
- * Get keys sorted by zIndex (ascending), then by key name
1185
- */
1186
- getSortedKeys() {
1187
- return Object.entries(this.stm).sort(([keyA, entryA], [keyB, entryB]) => {
1188
- const zIndexA = entryA.attributes.zIndex ?? 0;
1189
- const zIndexB = entryB.attributes.zIndex ?? 0;
1190
- if (zIndexA !== zIndexB) {
1191
- return zIndexA - zIndexB;
1192
- }
1193
- return keyA.localeCompare(keyB);
1194
- }).map(([key]) => key);
1195
- }
1196
- keys() {
1197
- return [...this.getSortedKeys(), "$date", "$time"];
1198
- }
1199
- values() {
1200
- const now = /* @__PURE__ */ new Date();
1201
- const sortedKeys = this.getSortedKeys();
1202
- const stmValues = sortedKeys.map((key) => this.stm[key].value);
1203
- return [
1204
- ...stmValues,
1205
- now.toISOString().split("T")[0],
1206
- now.toTimeString().split(" ")[0]
1207
- ];
1208
- }
1209
- entries() {
1210
- const now = /* @__PURE__ */ new Date();
1211
- const sortedKeys = this.getSortedKeys();
1212
- const stmEntries = sortedKeys.map(
1213
- (key) => [key, this.stm[key].value]
1214
- );
1215
- return [
1216
- ...stmEntries,
1217
- ["$date", now.toISOString().split("T")[0]],
1218
- ["$time", now.toTimeString().split(" ")[0]]
1219
- ];
1220
- }
1221
- size() {
1222
- return Object.keys(this.stm).length + 2;
1223
- }
1224
- getAll() {
1225
- const now = /* @__PURE__ */ new Date();
1226
- const result = {};
1227
- const sortedKeys = this.getSortedKeys();
1228
- sortedKeys.forEach((key) => {
1229
- result[key] = this.stm[key].value;
1230
- });
1231
- result["$date"] = now.toISOString().split("T")[0];
1232
- result["$time"] = now.toTimeString().split(" ")[0];
1233
- return result;
1234
- }
1235
- update(newValues) {
1236
- Object.entries(newValues).forEach(([key, value]) => {
1237
- if (key !== "$date" && key !== "$time") {
1238
- this.stm[key] = {
1239
- value,
1240
- attributes: { ...DEFAULT_KEY_ATTRIBUTES }
1241
- };
1242
- if (this.listeners[key]) {
1243
- this.listeners[key].forEach((listener) => listener(this.stm[key]?.value));
1089
+ const changeRatio = changedChars / Math.max(oldText.length, newText.length);
1090
+ if (changeRatio > diffThreshold) {
1091
+ this.doc.transact(() => {
1092
+ yText.delete(0, yText.length);
1093
+ yText.insert(0, newText);
1094
+ });
1095
+ return;
1096
+ }
1097
+ this.doc.transact(() => {
1098
+ let cursor = 0;
1099
+ for (const [op, text] of diffs) {
1100
+ if (op === 0) {
1101
+ cursor += text.length;
1102
+ } else if (op === -1) {
1103
+ yText.delete(cursor, text.length);
1104
+ } else if (op === 1) {
1105
+ yText.insert(cursor, text);
1106
+ cursor += text.length;
1244
1107
  }
1245
1108
  }
1246
1109
  });
1247
- this.notifyGlobalListeners();
1248
1110
  }
1111
+ // ... (subscribe methods)
1249
1112
  subscribe(key, listener) {
1250
1113
  if (!this.listeners[key]) {
1251
1114
  this.listeners[key] = [];
1252
1115
  }
1253
1116
  this.listeners[key].push(listener);
1254
- }
1255
- unsubscribe(key, listener) {
1256
- if (this.listeners[key]) {
1117
+ return () => {
1257
1118
  this.listeners[key] = this.listeners[key].filter((l) => l !== listener);
1258
- }
1119
+ };
1259
1120
  }
1260
1121
  subscribeToAll(listener) {
1261
1122
  this.globalListeners.push(listener);
1123
+ return () => {
1124
+ this.globalListeners = this.globalListeners.filter((l) => l !== listener);
1125
+ };
1262
1126
  }
1263
1127
  unsubscribeFromAll(listener) {
1264
1128
  this.globalListeners = this.globalListeners.filter((l) => l !== listener);
1265
1129
  }
1266
1130
  notifyGlobalListeners() {
1267
- this.globalListeners.forEach((listener) => listener());
1268
- }
1269
- injectSTM(template, _processingStack) {
1270
- if (template === null || template === void 0) {
1271
- return String(template);
1272
- }
1273
- const templateStr = String(template);
1274
- const keys = templateStr.match(/\{\{([$\w]+)\}\}/g);
1275
- if (!keys) {
1276
- return templateStr;
1277
- }
1278
- const cleanKeys = keys.map((key) => key.replace(/[{}]/g, ""));
1279
- const inputValues = cleanKeys.reduce((acc, key) => {
1280
- if (key === "$date" || key === "$time") {
1281
- return {
1282
- ...acc,
1283
- [key]: this.get_value(key, _processingStack)
1284
- };
1285
- }
1286
- const attributes = this.get_attributes(key);
1287
- if (_processingStack || attributes && attributes.visible) {
1288
- if (attributes && (attributes.type === "image" || attributes.type === "file")) {
1289
- return acc;
1290
- }
1291
- return {
1292
- ...acc,
1293
- [key]: this.get_value(key, _processingStack)
1294
- };
1295
- }
1296
- return acc;
1297
- }, {});
1298
- return templateStr.replace(/\{\{([$\w]+)\}\}/g, (match, key) => {
1299
- if (inputValues[key] !== void 0) {
1300
- return inputValues[key];
1301
- }
1302
- const attributes = this.get_attributes(key);
1303
- if (attributes && (attributes.type === "image" || attributes.type === "file")) {
1304
- return match;
1305
- }
1306
- return "";
1307
- });
1308
- }
1309
- getSTM() {
1310
- const now = /* @__PURE__ */ new Date();
1311
- const entries = [];
1312
- const sortedKeys = this.getSortedKeys();
1313
- sortedKeys.forEach((key) => {
1314
- const entry = this.stm[key];
1315
- if (entry.attributes.visible) {
1316
- entries.push([key, this.get_value(key)]);
1317
- }
1318
- });
1319
- entries.push(["$date", now.toISOString().split("T")[0]]);
1320
- entries.push(["$time", now.toTimeString().split(" ")[0]]);
1321
- return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
1131
+ this.globalListeners.forEach((l) => l());
1322
1132
  }
1323
- getSTMObject() {
1324
- return this.getAll();
1133
+ // Sanitize key name for use in tool names
1134
+ sanitizeKeyForTool(key) {
1135
+ return key.replace(/[^a-zA-Z0-9_-]/g, "_");
1325
1136
  }
1326
- getSTMForAPI() {
1327
- const now = /* @__PURE__ */ new Date();
1328
- const apiData = [];
1329
- const sortedKeys = this.getSortedKeys();
1330
- sortedKeys.forEach((key) => {
1331
- const entry = this.stm[key];
1332
- if (this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) {
1333
- const hasTemplate = entry.attributes.systemTags?.includes("ApplyTemplate") || entry.attributes.systemTags?.includes("template") || entry.attributes.template;
1334
- const processedValue = hasTemplate ? this.get_value(key) : entry.value;
1335
- apiData.push({
1336
- key,
1337
- value: processedValue,
1338
- type: entry.attributes.type,
1339
- contentType: entry.attributes.contentType
1340
- });
1137
+ // Find original key from sanitized tool name
1138
+ findKeyFromSanitizedTool(sanitizedKey) {
1139
+ for (const [key] of this.rootMap) {
1140
+ if (this.sanitizeKeyForTool(key) === sanitizedKey) {
1141
+ return key;
1341
1142
  }
1342
- });
1343
- apiData.push({
1344
- key: "$date",
1345
- value: now.toISOString().split("T")[0],
1346
- type: "text"
1347
- });
1348
- apiData.push({
1349
- key: "$time",
1350
- value: now.toTimeString().split(" ")[0],
1351
- type: "text"
1352
- });
1353
- return apiData;
1354
- }
1355
- getVisibleImages() {
1356
- const imageParts = [];
1357
- const sortedKeys = this.getSortedKeys();
1358
- sortedKeys.forEach((key) => {
1359
- const entry = this.stm[key];
1360
- if ((this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) && entry.attributes.type === "image" && entry.attributes.contentType) {
1361
- const dataUrl = this.createDataUrl(entry.value, entry.attributes.contentType);
1362
- imageParts.push({
1363
- type: "file",
1364
- mediaType: entry.attributes.contentType,
1365
- url: dataUrl,
1366
- filename: key
1367
- });
1368
- }
1369
- });
1370
- return imageParts;
1371
- }
1372
- toJSON() {
1373
- return JSON.stringify(this.serialize());
1374
- }
1375
- fromJSON(jsonString) {
1376
- try {
1377
- const data = JSON.parse(jsonString);
1378
- this.deserialize(data);
1379
- } catch (error) {
1380
- console.error("MindCache: Failed to deserialize JSON:", error);
1381
- }
1382
- }
1383
- serialize() {
1384
- const result = {};
1385
- const sortedKeys = this.getSortedKeys();
1386
- sortedKeys.forEach((key) => {
1387
- const entry = this.stm[key];
1388
- if (!entry.attributes.hardcoded) {
1389
- result[key] = {
1390
- value: entry.value,
1391
- attributes: { ...entry.attributes }
1392
- };
1393
- }
1394
- });
1395
- return result;
1396
- }
1397
- deserialize(data) {
1398
- if (typeof data === "object" && data !== null) {
1399
- this._isRemoteUpdate = true;
1400
- this.clear();
1401
- Object.entries(data).forEach(([key, entry]) => {
1402
- if (entry && typeof entry === "object" && "value" in entry && "attributes" in entry) {
1403
- const attrs = entry.attributes;
1404
- if (attrs.hardcoded === true || attrs.systemTags?.includes("protected")) {
1405
- return;
1406
- }
1407
- let systemTags = attrs.systemTags || [];
1408
- if (!attrs.systemTags || systemTags.length === 0) {
1409
- systemTags = [];
1410
- if (attrs.visible !== false) {
1411
- systemTags.push("prompt");
1412
- }
1413
- if (attrs.readonly) {
1414
- systemTags.push("readonly");
1415
- } else {
1416
- systemTags.push("LLMWrite");
1417
- }
1418
- if (attrs.hardcoded) {
1419
- systemTags.push("protected");
1420
- }
1421
- if (attrs.template) {
1422
- systemTags.push("ApplyTemplate");
1423
- }
1424
- }
1425
- systemTags = this.normalizeSystemTags(systemTags);
1426
- const contentTags = attrs.contentTags || attrs.tags || [];
1427
- this.stm[key] = {
1428
- value: entry.value,
1429
- attributes: {
1430
- ...attrs,
1431
- contentTags,
1432
- systemTags,
1433
- zIndex: attrs.zIndex ?? 0,
1434
- // Sync legacy attributes FROM normalized systemTags
1435
- tags: contentTags,
1436
- readonly: systemTags.includes("readonly") || !systemTags.includes("LLMWrite"),
1437
- visible: this.hasSystemPrompt(systemTags),
1438
- hardcoded: systemTags.includes("protected"),
1439
- template: systemTags.includes("ApplyTemplate") || systemTags.includes("template")
1440
- }
1441
- };
1442
- }
1443
- });
1444
- this.notifyGlobalListeners();
1445
- this._isRemoteUpdate = false;
1446
- }
1447
- }
1448
- get_system_prompt() {
1449
- const now = /* @__PURE__ */ new Date();
1450
- const promptLines = [];
1451
- const sortedKeys = this.getSortedKeys();
1452
- sortedKeys.forEach((key) => {
1453
- const entry = this.stm[key];
1454
- if (this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) {
1455
- if (entry.attributes.type === "image") {
1456
- promptLines.push(`image ${key} available`);
1457
- return;
1458
- }
1459
- if (entry.attributes.type === "file") {
1460
- const canWrite2 = this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
1461
- if (!canWrite2) {
1462
- promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}`);
1463
- } else {
1464
- const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
1465
- promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}. You can update this ${entry.attributes.type} using the write_${sanitizedKey} tool.`);
1466
- }
1467
- return;
1468
- }
1469
- const value = this.get_value(key);
1470
- const formattedValue = typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
1471
- const canWrite = this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
1472
- if (!canWrite) {
1473
- promptLines.push(`${key}: ${formattedValue}`);
1474
- } else {
1475
- const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
1476
- const toolInstruction = `You can rewrite "${key}" by using the write_${sanitizedKey} tool. This tool DOES NOT append \u2014 start your response with the old value (${formattedValue})`;
1477
- promptLines.push(`${key}: ${formattedValue}. ${toolInstruction}`);
1478
- }
1479
- }
1480
- });
1481
- promptLines.push(`$date: ${now.toISOString().split("T")[0]}`);
1482
- promptLines.push(`$time: ${now.toTimeString().split(" ")[0]}`);
1483
- return promptLines.join("\n");
1484
- }
1485
- findKeyFromToolName(toolName) {
1486
- if (!toolName.startsWith("write_")) {
1487
- return void 0;
1488
1143
  }
1489
- const sanitizedKey = toolName.replace("write_", "");
1490
- const sortedKeys = this.getSortedKeys();
1491
- return sortedKeys.find(
1492
- (k) => k.replace(/[^a-zA-Z0-9_-]/g, "_") === sanitizedKey
1493
- );
1144
+ return void 0;
1494
1145
  }
1146
+ /**
1147
+ * Generate Vercel AI SDK compatible tools for writable keys.
1148
+ * For document type keys, generates additional tools: append_, insert_, edit_
1149
+ */
1495
1150
  get_aisdk_tools() {
1496
1151
  const tools = {};
1497
- const sortedKeys = this.getSortedKeys();
1498
- const writableKeys = sortedKeys.filter((key) => {
1499
- const entry = this.stm[key];
1500
- return this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
1501
- });
1502
- writableKeys.forEach((key) => {
1503
- const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
1504
- const toolName = `write_${sanitizedKey}`;
1505
- const entry = this.stm[key];
1506
- const keyType = entry?.attributes.type || "text";
1507
- let inputSchema;
1508
- let description = `Write a value to the STM key: ${key}`;
1509
- if (keyType === "image" || keyType === "file") {
1510
- description += " (expects base64 encoded data)";
1511
- inputSchema = z.object({
1512
- value: z.string().describe(`Base64 encoded data for ${key}`),
1513
- contentType: z.string().optional().describe(`MIME type for the ${keyType}`)
1514
- });
1515
- } else if (keyType === "json") {
1516
- description += " (expects JSON string)";
1517
- inputSchema = z.object({
1518
- value: z.string().describe(`JSON string value for ${key}`)
1519
- });
1520
- } else {
1521
- inputSchema = z.object({
1522
- value: z.string().describe(`The text value to write to ${key}`)
1523
- });
1152
+ for (const [key, val] of this.rootMap) {
1153
+ if (key.startsWith("$")) {
1154
+ continue;
1524
1155
  }
1525
- tools[toolName] = {
1526
- description,
1527
- inputSchema,
1528
- execute: async (input) => {
1529
- if (keyType === "image" || keyType === "file") {
1530
- if (input.contentType) {
1531
- this.set_base64(key, input.value, input.contentType, keyType);
1532
- } else {
1533
- const existingContentType = entry?.attributes.contentType;
1534
- if (existingContentType) {
1535
- this.set_base64(key, input.value, existingContentType, keyType);
1536
- } else {
1537
- throw new Error(`Content type required for ${keyType} data`);
1538
- }
1539
- }
1540
- } else {
1541
- this.set_value(key, input.value);
1542
- }
1543
- let resultMessage;
1544
- if (keyType === "image") {
1545
- resultMessage = `Successfully saved image to ${key}`;
1546
- } else if (keyType === "file") {
1547
- resultMessage = `Successfully saved file to ${key}`;
1548
- } else if (keyType === "json") {
1549
- resultMessage = `Successfully saved JSON data to ${key}`;
1156
+ const entryMap = val;
1157
+ const attributes = entryMap.get("attributes");
1158
+ const isWritable = !attributes?.readonly && (attributes?.systemTags?.includes("LLMWrite") || !attributes?.systemTags);
1159
+ if (!isWritable) {
1160
+ continue;
1161
+ }
1162
+ const sanitizedKey = this.sanitizeKeyForTool(key);
1163
+ const isDocument = attributes?.type === "document";
1164
+ tools[`write_${sanitizedKey}`] = {
1165
+ description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
1166
+ inputSchema: {
1167
+ type: "object",
1168
+ properties: {
1169
+ value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1170
+ },
1171
+ required: ["value"]
1172
+ },
1173
+ execute: async ({ value }) => {
1174
+ if (isDocument) {
1175
+ this.replace_document_text(key, value);
1550
1176
  } else {
1551
- resultMessage = `Successfully wrote "${input.value}" to ${key}`;
1177
+ this.set_value(key, value);
1552
1178
  }
1553
1179
  return {
1554
- result: resultMessage,
1180
+ result: `Successfully wrote "${value}" to ${key}`,
1555
1181
  key,
1556
- value: input.value,
1557
- type: keyType,
1558
- contentType: input.contentType,
1559
- sanitizedKey
1182
+ value
1560
1183
  };
1561
1184
  }
1562
1185
  };
1563
- });
1564
- if (writableKeys.length === 0) {
1565
- return {};
1186
+ if (isDocument) {
1187
+ tools[`append_${sanitizedKey}`] = {
1188
+ description: `Append text to the end of "${key}" document`,
1189
+ inputSchema: {
1190
+ type: "object",
1191
+ properties: {
1192
+ text: { type: "string", description: "Text to append" }
1193
+ },
1194
+ required: ["text"]
1195
+ },
1196
+ execute: async ({ text }) => {
1197
+ const yText = this.get_document(key);
1198
+ if (yText) {
1199
+ yText.insert(yText.length, text);
1200
+ return {
1201
+ result: `Successfully appended to ${key}`,
1202
+ key,
1203
+ appended: text
1204
+ };
1205
+ }
1206
+ return { result: `Document ${key} not found`, key };
1207
+ }
1208
+ };
1209
+ tools[`insert_${sanitizedKey}`] = {
1210
+ description: `Insert text at a position in "${key}" document`,
1211
+ inputSchema: {
1212
+ type: "object",
1213
+ properties: {
1214
+ index: { type: "number", description: "Position to insert at (0 = start)" },
1215
+ text: { type: "string", description: "Text to insert" }
1216
+ },
1217
+ required: ["index", "text"]
1218
+ },
1219
+ execute: async ({ index, text }) => {
1220
+ this.insert_text(key, index, text);
1221
+ return {
1222
+ result: `Successfully inserted text at position ${index} in ${key}`,
1223
+ key,
1224
+ index,
1225
+ inserted: text
1226
+ };
1227
+ }
1228
+ };
1229
+ tools[`edit_${sanitizedKey}`] = {
1230
+ description: `Find and replace text in "${key}" document`,
1231
+ inputSchema: {
1232
+ type: "object",
1233
+ properties: {
1234
+ find: { type: "string", description: "Text to find" },
1235
+ replace: { type: "string", description: "Replacement text" }
1236
+ },
1237
+ required: ["find", "replace"]
1238
+ },
1239
+ execute: async ({ find, replace }) => {
1240
+ const yText = this.get_document(key);
1241
+ if (yText) {
1242
+ const text = yText.toString();
1243
+ const idx = text.indexOf(find);
1244
+ if (idx !== -1) {
1245
+ yText.delete(idx, find.length);
1246
+ yText.insert(idx, replace);
1247
+ return {
1248
+ result: `Successfully replaced "${find}" with "${replace}" in ${key}`,
1249
+ key,
1250
+ find,
1251
+ replace,
1252
+ index: idx
1253
+ };
1254
+ }
1255
+ return { result: `Text "${find}" not found in ${key}`, key };
1256
+ }
1257
+ return { result: `Document ${key} not found`, key };
1258
+ }
1259
+ };
1260
+ }
1566
1261
  }
1567
1262
  return tools;
1568
1263
  }
1569
- executeToolCall(toolName, value) {
1570
- const originalKey = this.findKeyFromToolName(toolName);
1571
- if (!originalKey) {
1572
- return null;
1573
- }
1574
- const entry = this.stm[originalKey];
1575
- const canWrite = entry && (this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly"));
1576
- if (!canWrite) {
1577
- return null;
1578
- }
1579
- this.set_value(originalKey, value);
1580
- return {
1581
- result: `Successfully wrote "${value}" to ${originalKey}`,
1582
- key: originalKey,
1583
- value
1584
- };
1585
- }
1586
- // ============================================
1587
- // Content Tag Methods (available to all access levels)
1588
- // ============================================
1589
- /**
1590
- * Add a content tag to a key (user-level organization)
1591
- */
1592
- addTag(key, tag) {
1593
- if (key === "$date" || key === "$time") {
1594
- return false;
1595
- }
1596
- const entry = this.stm[key];
1597
- if (!entry) {
1598
- return false;
1599
- }
1600
- if (!entry.attributes.contentTags) {
1601
- entry.attributes.contentTags = [];
1602
- }
1603
- if (!entry.attributes.contentTags.includes(tag)) {
1604
- entry.attributes.contentTags.push(tag);
1605
- entry.attributes.tags = [...entry.attributes.contentTags];
1606
- this.notifyGlobalListeners();
1607
- return true;
1608
- }
1609
- return false;
1610
- }
1611
- /**
1612
- * Remove a content tag from a key
1613
- */
1614
- removeTag(key, tag) {
1615
- if (key === "$date" || key === "$time") {
1616
- return false;
1617
- }
1618
- const entry = this.stm[key];
1619
- if (!entry || !entry.attributes.contentTags) {
1620
- return false;
1621
- }
1622
- const tagIndex = entry.attributes.contentTags.indexOf(tag);
1623
- if (tagIndex > -1) {
1624
- entry.attributes.contentTags.splice(tagIndex, 1);
1625
- entry.attributes.tags = [...entry.attributes.contentTags];
1626
- this.notifyGlobalListeners();
1627
- return true;
1628
- }
1629
- return false;
1630
- }
1631
- /**
1632
- * Get all content tags for a key
1633
- */
1634
- getTags(key) {
1635
- if (key === "$date" || key === "$time") {
1636
- return [];
1637
- }
1638
- const entry = this.stm[key];
1639
- return entry?.attributes.contentTags || [];
1640
- }
1641
1264
  /**
1642
- * Get all unique content tags across all keys
1265
+ * Generate a system prompt containing all visible STM keys and their values.
1266
+ * Indicates which tools can be used to modify writable keys.
1643
1267
  */
1644
- getAllTags() {
1645
- const allTags = /* @__PURE__ */ new Set();
1646
- Object.values(this.stm).forEach((entry) => {
1647
- if (entry.attributes.contentTags) {
1648
- entry.attributes.contentTags.forEach((tag) => allTags.add(tag));
1268
+ get_system_prompt() {
1269
+ const lines = [];
1270
+ for (const [key, val] of this.rootMap) {
1271
+ if (key.startsWith("$")) {
1272
+ continue;
1649
1273
  }
1650
- });
1651
- return Array.from(allTags);
1652
- }
1653
- /**
1654
- * Check if a key has a specific content tag
1655
- */
1656
- hasTag(key, tag) {
1657
- if (key === "$date" || key === "$time") {
1658
- return false;
1659
- }
1660
- const entry = this.stm[key];
1661
- return entry?.attributes.contentTags?.includes(tag) || false;
1662
- }
1663
- /**
1664
- * Get all keys with a specific content tag as formatted string
1665
- */
1666
- getTagged(tag) {
1667
- const entries = [];
1668
- const sortedKeys = this.getSortedKeys();
1669
- sortedKeys.forEach((key) => {
1670
- const entry = this.stm[key];
1671
- if (entry.attributes.contentTags?.includes(tag)) {
1672
- entries.push([key, this.get_value(key)]);
1274
+ const entryMap = val;
1275
+ const attributes = entryMap.get("attributes");
1276
+ const isVisible = attributes?.visible !== false && (attributes?.systemTags?.includes("prompt") || attributes?.systemTags?.includes("SystemPrompt") || !attributes?.systemTags);
1277
+ if (!isVisible) {
1278
+ continue;
1673
1279
  }
1674
- });
1675
- return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
1676
- }
1677
- /**
1678
- * Get all keys with a specific content tag
1679
- */
1680
- getKeysByTag(tag) {
1681
- const sortedKeys = this.getSortedKeys();
1682
- return sortedKeys.filter((key) => {
1683
- const entry = this.stm[key];
1684
- return entry.attributes.contentTags?.includes(tag);
1685
- });
1686
- }
1687
- // ============================================
1688
- // System Tag Methods (requires system access level)
1689
- // ============================================
1690
- /**
1691
- * Add a system tag to a key (requires system access)
1692
- * System tags: 'prompt', 'readonly', 'protected', 'template'
1693
- */
1694
- systemAddTag(key, tag) {
1695
- if (!this.hasSystemAccess) {
1696
- console.warn("MindCache: systemAddTag requires system access level");
1697
- return false;
1698
- }
1699
- if (key === "$date" || key === "$time") {
1700
- return false;
1701
- }
1702
- const entry = this.stm[key];
1703
- if (!entry) {
1704
- return false;
1705
- }
1706
- if (!entry.attributes.systemTags) {
1707
- entry.attributes.systemTags = [];
1708
- }
1709
- if (!entry.attributes.systemTags.includes(tag)) {
1710
- entry.attributes.systemTags.push(tag);
1711
- this.syncLegacyFromSystemTags(entry);
1712
- this.notifyGlobalListeners();
1713
- return true;
1714
- }
1715
- return false;
1716
- }
1717
- /**
1718
- * Remove a system tag from a key (requires system access)
1719
- */
1720
- systemRemoveTag(key, tag) {
1721
- if (!this.hasSystemAccess) {
1722
- console.warn("MindCache: systemRemoveTag requires system access level");
1723
- return false;
1724
- }
1725
- if (key === "$date" || key === "$time") {
1726
- return false;
1727
- }
1728
- const entry = this.stm[key];
1729
- if (!entry || !entry.attributes.systemTags) {
1730
- return false;
1731
- }
1732
- const isHardcoded = entry.attributes.hardcoded || entry.attributes.systemTags.includes("protected");
1733
- if (tag === "protected" && isHardcoded) {
1734
- return false;
1735
- }
1736
- const tagIndex = entry.attributes.systemTags.indexOf(tag);
1737
- if (tagIndex > -1) {
1738
- entry.attributes.systemTags.splice(tagIndex, 1);
1739
- this.syncLegacyFromSystemTags(entry);
1740
- if (isHardcoded) {
1741
- if (!entry.attributes.systemTags.includes("protected")) {
1742
- entry.attributes.systemTags.push("protected");
1280
+ const value = this.get_value(key);
1281
+ const displayValue = typeof value === "object" ? JSON.stringify(value) : value;
1282
+ const isWritable = !attributes?.readonly && (attributes?.systemTags?.includes("LLMWrite") || !attributes?.systemTags);
1283
+ const isDocument = attributes?.type === "document";
1284
+ const sanitizedKey = this.sanitizeKeyForTool(key);
1285
+ if (isWritable) {
1286
+ if (isDocument) {
1287
+ lines.push(`${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`);
1288
+ } else {
1289
+ const oldValueHint = displayValue ? ` This tool DOES NOT append \u2014 start your response with the old value (${displayValue})` : "";
1290
+ lines.push(`${key}: ${displayValue}. You can rewrite "${key}" by using the write_${sanitizedKey} tool.${oldValueHint}`);
1743
1291
  }
1744
- entry.attributes.hardcoded = true;
1745
- entry.attributes.readonly = true;
1746
- entry.attributes.template = false;
1292
+ } else {
1293
+ lines.push(`${key}: ${displayValue}`);
1747
1294
  }
1748
- this.notifyGlobalListeners();
1749
- return true;
1750
- }
1751
- return false;
1752
- }
1753
- /**
1754
- * Get all system tags for a key (requires system access)
1755
- */
1756
- systemGetTags(key) {
1757
- if (!this.hasSystemAccess) {
1758
- console.warn("MindCache: systemGetTags requires system access level");
1759
- return [];
1760
- }
1761
- if (key === "$date" || key === "$time") {
1762
- return [];
1763
1295
  }
1764
- const entry = this.stm[key];
1765
- return entry?.attributes.systemTags || [];
1296
+ lines.push(`$date: ${this.get_value("$date")}`);
1297
+ lines.push(`$time: ${this.get_value("$time")}`);
1298
+ return lines.join(", ");
1766
1299
  }
1767
1300
  /**
1768
- * Check if a key has a specific system tag (requires system access)
1301
+ * Execute a tool call by name with the given value.
1302
+ * Returns the result or null if tool not found.
1769
1303
  */
1770
- systemHasTag(key, tag) {
1771
- if (!this.hasSystemAccess) {
1772
- console.warn("MindCache: systemHasTag requires system access level");
1773
- return false;
1774
- }
1775
- if (key === "$date" || key === "$time") {
1776
- return false;
1777
- }
1778
- const entry = this.stm[key];
1779
- return entry?.attributes.systemTags?.includes(tag) || false;
1780
- }
1781
- /**
1782
- * Set all system tags for a key at once (requires system access)
1783
- */
1784
- systemSetTags(key, tags) {
1785
- if (!this.hasSystemAccess) {
1786
- console.warn("MindCache: systemSetTags requires system access level");
1787
- return false;
1788
- }
1789
- if (key === "$date" || key === "$time") {
1790
- return false;
1304
+ executeToolCall(toolName, value) {
1305
+ const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
1306
+ if (!match) {
1307
+ return null;
1791
1308
  }
1792
- const entry = this.stm[key];
1793
- if (!entry) {
1794
- return false;
1309
+ const [, action, sanitizedKey] = match;
1310
+ const key = this.findKeyFromSanitizedTool(sanitizedKey);
1311
+ if (!key) {
1312
+ return null;
1795
1313
  }
1796
- entry.attributes.systemTags = [...tags];
1797
- this.syncLegacyFromSystemTags(entry);
1798
- this.notifyGlobalListeners();
1799
- return true;
1800
- }
1801
- /**
1802
- * Get all keys with a specific system tag (requires system access)
1803
- */
1804
- systemGetKeysByTag(tag) {
1805
- if (!this.hasSystemAccess) {
1806
- console.warn("MindCache: systemGetKeysByTag requires system access level");
1807
- return [];
1314
+ const entryMap = this.rootMap.get(key);
1315
+ if (!entryMap) {
1316
+ return null;
1808
1317
  }
1809
- const sortedKeys = this.getSortedKeys();
1810
- return sortedKeys.filter((key) => {
1811
- const entry = this.stm[key];
1812
- return entry.attributes.systemTags?.includes(tag);
1813
- });
1814
- }
1815
- /**
1816
- * Helper to sync legacy boolean attributes from system tags
1817
- */
1818
- syncLegacyFromSystemTags(entry) {
1819
- const tags = entry.attributes.systemTags || [];
1820
- entry.attributes.readonly = tags.includes("readonly");
1821
- entry.attributes.visible = tags.includes("prompt");
1822
- entry.attributes.hardcoded = tags.includes("protected");
1823
- entry.attributes.template = tags.includes("ApplyTemplate") || tags.includes("template");
1824
- }
1825
- toMarkdown() {
1826
- const now = /* @__PURE__ */ new Date();
1827
- const lines = [];
1828
- const appendixEntries = [];
1829
- let appendixCounter = 0;
1830
- lines.push("# MindCache STM Export");
1831
- lines.push("");
1832
- lines.push(`Export Date: ${now.toISOString().split("T")[0]}`);
1833
- lines.push("");
1834
- lines.push("---");
1835
- lines.push("");
1836
- lines.push("## STM Entries");
1837
- lines.push("");
1838
- const sortedKeys = this.getSortedKeys();
1839
- sortedKeys.forEach((key) => {
1840
- const entry = this.stm[key];
1841
- if (entry.attributes.hardcoded) {
1842
- return;
1843
- }
1844
- lines.push(`### ${key}`);
1845
- const entryType = entry.attributes.type && entry.attributes.type !== "undefined" ? entry.attributes.type : "text";
1846
- lines.push(`- **Type**: \`${entryType}\``);
1847
- lines.push(`- **Readonly**: \`${entry.attributes.readonly}\``);
1848
- lines.push(`- **Visible**: \`${entry.attributes.visible}\``);
1849
- lines.push(`- **Template**: \`${entry.attributes.template}\``);
1850
- lines.push(`- **Z-Index**: \`${entry.attributes.zIndex ?? 0}\``);
1851
- if (entry.attributes.tags && entry.attributes.tags.length > 0) {
1852
- lines.push(`- **Tags**: \`${entry.attributes.tags.join("`, `")}\``);
1853
- }
1854
- if (entry.attributes.contentType) {
1855
- lines.push(`- **Content Type**: \`${entry.attributes.contentType}\``);
1856
- }
1857
- if (entryType === "image" || entryType === "file") {
1858
- const label = String.fromCharCode(65 + appendixCounter);
1859
- appendixCounter++;
1860
- lines.push(`- **Value**: [See Appendix ${label}]`);
1861
- appendixEntries.push({
1862
- key,
1863
- type: entryType,
1864
- contentType: entry.attributes.contentType || "application/octet-stream",
1865
- base64: entry.value,
1866
- label
1867
- });
1868
- } else if (entryType === "json") {
1869
- lines.push("- **Value**:");
1870
- lines.push("```json");
1871
- try {
1872
- const jsonValue = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value, null, 2);
1873
- lines.push(jsonValue);
1874
- } catch {
1875
- lines.push(String(entry.value));
1876
- }
1877
- lines.push("```");
1878
- } else {
1879
- const valueStr = String(entry.value);
1880
- lines.push("- **Value**:");
1881
- lines.push("```");
1882
- lines.push(valueStr);
1883
- lines.push("```");
1884
- }
1885
- lines.push("");
1886
- lines.push("---");
1887
- lines.push("");
1888
- });
1889
- if (appendixEntries.length > 0) {
1890
- lines.push("## Appendix: Binary Data");
1891
- lines.push("");
1892
- appendixEntries.forEach(({ key, contentType, base64, label }) => {
1893
- lines.push(`### Appendix ${label}: ${key}`);
1894
- lines.push(`**Type**: ${contentType}`);
1895
- lines.push("");
1896
- lines.push("```");
1897
- lines.push(base64);
1898
- lines.push("```");
1899
- lines.push("");
1900
- lines.push("---");
1901
- lines.push("");
1902
- });
1318
+ const attributes = entryMap.get("attributes");
1319
+ const isWritable = !attributes?.readonly && (attributes?.systemTags?.includes("LLMWrite") || !attributes?.systemTags);
1320
+ if (!isWritable) {
1321
+ return null;
1903
1322
  }
1904
- lines.push("*End of MindCache Export*");
1905
- return lines.join("\n");
1906
- }
1907
- fromMarkdown(markdown) {
1908
- const lines = markdown.split("\n");
1909
- let currentSection = "header";
1910
- let currentKey = null;
1911
- let currentEntry = null;
1912
- let inCodeBlock = false;
1913
- let codeBlockContent = [];
1914
- let codeBlockType = null;
1915
- const appendixData = {};
1916
- let currentAppendixKey = null;
1917
- const pendingEntries = {};
1918
- this.clear();
1919
- for (let i = 0; i < lines.length; i++) {
1920
- const line = lines[i];
1921
- const trimmed = line.trim();
1922
- if (trimmed === "## STM Entries") {
1923
- currentSection = "entries";
1924
- continue;
1925
- }
1926
- if (trimmed === "## Appendix: Binary Data") {
1927
- currentSection = "appendix";
1928
- continue;
1929
- }
1930
- if (trimmed === "```" || trimmed === "```json") {
1931
- if (!inCodeBlock) {
1932
- inCodeBlock = true;
1933
- codeBlockContent = [];
1934
- codeBlockType = currentSection === "appendix" ? "base64" : trimmed === "```json" ? "json" : "value";
1323
+ const isDocument = attributes?.type === "document";
1324
+ switch (action) {
1325
+ case "write":
1326
+ if (isDocument) {
1327
+ this.replace_document_text(key, value);
1935
1328
  } else {
1936
- inCodeBlock = false;
1937
- const content = codeBlockContent.join("\n");
1938
- if (currentSection === "appendix" && currentAppendixKey) {
1939
- appendixData[currentAppendixKey].base64 = content;
1940
- } else if (currentEntry && codeBlockType === "json") {
1941
- currentEntry.value = content;
1942
- } else if (currentEntry && codeBlockType === "value") {
1943
- currentEntry.value = content;
1944
- }
1945
- codeBlockContent = [];
1946
- codeBlockType = null;
1329
+ this.set_value(key, value);
1947
1330
  }
1948
- continue;
1949
- }
1950
- if (inCodeBlock) {
1951
- codeBlockContent.push(line);
1952
- continue;
1953
- }
1954
- if (currentSection === "entries") {
1955
- if (trimmed.startsWith("### ")) {
1956
- if (currentKey && currentEntry && currentEntry.attributes) {
1957
- pendingEntries[currentKey] = currentEntry;
1331
+ return {
1332
+ result: `Successfully wrote "${value}" to ${key}`,
1333
+ key,
1334
+ value
1335
+ };
1336
+ case "append":
1337
+ if (isDocument) {
1338
+ const yText = this.get_document(key);
1339
+ if (yText) {
1340
+ yText.insert(yText.length, value);
1341
+ return {
1342
+ result: `Successfully appended to ${key}`,
1343
+ key,
1344
+ value
1345
+ };
1958
1346
  }
1959
- currentKey = trimmed.substring(4);
1960
- currentEntry = {
1961
- value: void 0,
1962
- attributes: {
1963
- ...DEFAULT_KEY_ATTRIBUTES,
1964
- contentTags: [],
1965
- systemTags: ["prompt"],
1966
- tags: []
1967
- }
1347
+ }
1348
+ return null;
1349
+ case "insert":
1350
+ if (isDocument && typeof value === "object" && value.index !== void 0 && value.text) {
1351
+ this.insert_text(key, value.index, value.text);
1352
+ return {
1353
+ result: `Successfully inserted at position ${value.index} in ${key}`,
1354
+ key,
1355
+ value: value.text
1968
1356
  };
1969
- } else if (trimmed.startsWith("- **Type**: `")) {
1970
- const type = trimmed.match(/`([^`]+)`/)?.[1];
1971
- if (currentEntry && type && type !== "undefined") {
1972
- currentEntry.attributes.type = type;
1973
- }
1974
- } else if (trimmed.startsWith("- **Readonly**: `")) {
1975
- const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
1976
- if (currentEntry) {
1977
- currentEntry.attributes.readonly = value;
1978
- }
1979
- } else if (trimmed.startsWith("- **Visible**: `")) {
1980
- const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
1981
- if (currentEntry) {
1982
- currentEntry.attributes.visible = value;
1983
- }
1984
- } else if (trimmed.startsWith("- **Template**: `")) {
1985
- const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
1986
- if (currentEntry) {
1987
- currentEntry.attributes.template = value;
1988
- }
1989
- } else if (trimmed.startsWith("- **Z-Index**: `")) {
1990
- const zIndexStr = trimmed.match(/`([^`]+)`/)?.[1];
1991
- if (currentEntry && zIndexStr) {
1992
- const zIndex = parseInt(zIndexStr, 10);
1993
- if (!isNaN(zIndex)) {
1994
- currentEntry.attributes.zIndex = zIndex;
1995
- }
1996
- }
1997
- } else if (trimmed.startsWith("- **Tags**: `")) {
1998
- const tagsStr = trimmed.substring(13, trimmed.length - 1);
1999
- if (currentEntry) {
2000
- currentEntry.attributes.tags = tagsStr.split("`, `");
2001
- }
2002
- } else if (trimmed.startsWith("- **Content Type**: `")) {
2003
- const contentType = trimmed.match(/`([^`]+)`/)?.[1];
2004
- if (currentEntry && contentType) {
2005
- currentEntry.attributes.contentType = contentType;
2006
- }
2007
- } else if (trimmed.startsWith("- **Value**: `")) {
2008
- const value = trimmed.substring(14, trimmed.length - 1);
2009
- if (currentEntry) {
2010
- currentEntry.value = value;
2011
- }
2012
- } else if (trimmed.startsWith("- **Value**: [See Appendix ")) {
2013
- const labelMatch = trimmed.match(/Appendix ([A-Z])\]/);
2014
- if (currentEntry && labelMatch && currentKey) {
2015
- currentEntry.appendixLabel = labelMatch[1];
2016
- currentEntry.value = "";
2017
- }
2018
1357
  }
2019
- }
2020
- if (currentSection === "appendix") {
2021
- if (trimmed.startsWith("### Appendix ")) {
2022
- const match = trimmed.match(/### Appendix ([A-Z]): (.+)/);
2023
- if (match) {
2024
- const label = match[1];
2025
- const key = match[2];
2026
- currentAppendixKey = `${label}:${key}`;
2027
- appendixData[currentAppendixKey] = { contentType: "", base64: "" };
2028
- }
2029
- } else if (trimmed.startsWith("**Type**: ")) {
2030
- const contentType = trimmed.substring(10);
2031
- if (currentAppendixKey) {
2032
- appendixData[currentAppendixKey].contentType = contentType;
1358
+ return null;
1359
+ case "edit":
1360
+ if (isDocument && typeof value === "object" && value.find && value.replace !== void 0) {
1361
+ const yText = this.get_document(key);
1362
+ if (yText) {
1363
+ const text = yText.toString();
1364
+ const idx = text.indexOf(value.find);
1365
+ if (idx !== -1) {
1366
+ yText.delete(idx, value.find.length);
1367
+ yText.insert(idx, value.replace);
1368
+ return {
1369
+ result: `Successfully replaced "${value.find}" with "${value.replace}" in ${key}`,
1370
+ key,
1371
+ value: value.replace
1372
+ };
1373
+ }
2033
1374
  }
2034
1375
  }
2035
- }
1376
+ return null;
1377
+ default:
1378
+ return null;
2036
1379
  }
2037
- if (currentKey && currentEntry && currentEntry.attributes) {
2038
- pendingEntries[currentKey] = currentEntry;
2039
- }
2040
- Object.entries(pendingEntries).forEach(([key, entry]) => {
2041
- const appendixLabel = entry.appendixLabel;
2042
- if (appendixLabel) {
2043
- const appendixKey = `${appendixLabel}:${key}`;
2044
- const appendixInfo = appendixData[appendixKey];
2045
- if (appendixInfo && appendixInfo.base64) {
2046
- entry.value = appendixInfo.base64;
2047
- if (!entry.attributes.contentType && appendixInfo.contentType) {
2048
- entry.attributes.contentType = appendixInfo.contentType;
2049
- }
2050
- }
2051
- }
2052
- if (entry.value !== void 0 && entry.attributes) {
2053
- const attrs = entry.attributes;
2054
- if (attrs.tags && attrs.tags.length > 0 && (!attrs.contentTags || attrs.contentTags.length === 0)) {
2055
- attrs.contentTags = [...attrs.tags];
2056
- }
2057
- if (!attrs.systemTags || attrs.systemTags.length === 0) {
2058
- const systemTags = [];
2059
- if (attrs.visible !== false) {
2060
- systemTags.push("prompt");
2061
- }
2062
- if (attrs.readonly) {
2063
- systemTags.push("readonly");
2064
- } else {
2065
- systemTags.push("LLMWrite");
2066
- }
2067
- if (attrs.hardcoded) {
2068
- systemTags.push("protected");
2069
- }
2070
- if (attrs.template) {
2071
- systemTags.push("template");
2072
- }
2073
- attrs.systemTags = this.normalizeSystemTags(systemTags);
2074
- } else {
2075
- attrs.systemTags = this.normalizeSystemTags(attrs.systemTags);
2076
- }
2077
- if (!attrs.contentTags) {
2078
- attrs.contentTags = [];
2079
- }
2080
- if (!attrs.tags) {
2081
- attrs.tags = [...attrs.contentTags];
2082
- }
2083
- const normalizedTags = attrs.systemTags || [];
2084
- attrs.readonly = normalizedTags.includes("readonly") || !normalizedTags.includes("LLMWrite");
2085
- attrs.visible = this.hasSystemPrompt(normalizedTags);
2086
- attrs.hardcoded = normalizedTags.includes("protected");
2087
- attrs.template = normalizedTags.includes("ApplyTemplate") || normalizedTags.includes("template");
2088
- this.stm[key] = {
2089
- value: entry.value,
2090
- attributes: attrs
2091
- };
2092
- }
2093
- });
2094
- this.notifyGlobalListeners();
1380
+ }
1381
+ // Internal method stub for legacy compatibility
1382
+ _setFromRemote(_key, _value, _attributes) {
1383
+ }
1384
+ _deleteFromRemote(_key) {
1385
+ }
1386
+ _clearFromRemote() {
2095
1387
  }
2096
1388
  };
2097
- var mindcache = new MindCache();
2098
1389
 
2099
1390
  // src/cloud/index.ts
2100
1391
  init_CloudAdapter();
@@ -2137,6 +1428,6 @@ function useMindCache(options) {
2137
1428
  };
2138
1429
  }
2139
1430
 
2140
- export { CloudAdapter, DEFAULT_KEY_ATTRIBUTES, IndexedDBAdapter, MindCache, mindcache, useMindCache };
1431
+ export { CloudAdapter, DEFAULT_KEY_ATTRIBUTES, IndexedDBAdapter, MindCache, useMindCache };
2141
1432
  //# sourceMappingURL=index.mjs.map
2142
1433
  //# sourceMappingURL=index.mjs.map