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