mindcache 3.4.2 → 3.4.4

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.
@@ -177,6 +177,7 @@ var init_CloudAdapter = __esm({
177
177
  RECONNECT_DELAY = 1e3;
178
178
  MAX_RECONNECT_DELAY = 3e4;
179
179
  CloudAdapter = class {
180
+ // Track if initial sync is complete
180
181
  constructor(config) {
181
182
  this.config = config;
182
183
  if (!config.baseUrl) {
@@ -196,6 +197,7 @@ var init_CloudAdapter = __esm({
196
197
  token = null;
197
198
  handleOnline = null;
198
199
  handleOffline = null;
200
+ _synced = false;
199
201
  /** Browser network status - instantly updated via navigator.onLine */
200
202
  get isOnline() {
201
203
  return this._isOnline;
@@ -378,10 +380,14 @@ var init_CloudAdapter = __esm({
378
380
  const encoder = encoding.createEncoder();
379
381
  const decoder = decoding.createDecoder(new Uint8Array(event.data));
380
382
  if (this.mindcache) {
381
- syncProtocol.readSyncMessage(decoder, encoder, this.mindcache.doc, this);
383
+ const messageType = syncProtocol.readSyncMessage(decoder, encoder, this.mindcache.doc, this);
382
384
  if (encoding.length(encoder) > 0) {
383
385
  this.sendBinary(encoding.toUint8Array(encoder));
384
386
  }
387
+ if (!this._synced && (messageType === 1 || messageType === 2)) {
388
+ this._synced = true;
389
+ this.emit("synced");
390
+ }
385
391
  }
386
392
  }
387
393
  } catch (error) {
@@ -430,134 +436,1001 @@ var DEFAULT_KEY_ATTRIBUTES = {
430
436
  zIndex: 0
431
437
  };
432
438
 
433
- // src/core/MindCache.ts
434
- var MindCache = class {
435
- // Public doc for adapter access
436
- doc;
437
- rootMap;
438
- // Key -> EntryMap({value, attributes})
439
- // Cache listeners
440
- listeners = {};
441
- globalListeners = [];
442
- // Metadata
443
- version = "3.3.2";
444
- // Internal flag to prevent sync loops when receiving remote updates
445
- // (Less critical with Yjs but kept for API compat)
446
- normalizeSystemTags(tags) {
447
- const normalized = [];
448
- const seen = /* @__PURE__ */ new Set();
449
- for (const tag of tags) {
450
- if (["SystemPrompt", "LLMRead", "LLMWrite", "protected", "ApplyTemplate"].includes(tag)) {
451
- if (!seen.has(tag)) {
452
- seen.add(tag);
453
- normalized.push(tag);
439
+ // src/core/MarkdownSerializer.ts
440
+ var MarkdownSerializer = class {
441
+ /**
442
+ * Export MindCache data to Markdown format.
443
+ */
444
+ static toMarkdown(mc) {
445
+ const now = /* @__PURE__ */ new Date();
446
+ const lines = [];
447
+ const appendixEntries = [];
448
+ let appendixCounter = 0;
449
+ lines.push("# MindCache STM Export");
450
+ lines.push("");
451
+ lines.push(`Export Date: ${now.toISOString().split("T")[0]}`);
452
+ lines.push("");
453
+ lines.push("---");
454
+ lines.push("");
455
+ lines.push("## STM Entries");
456
+ lines.push("");
457
+ const sortedKeys = mc.getSortedKeys();
458
+ sortedKeys.forEach((key) => {
459
+ const attributes = mc.get_attributes(key);
460
+ const value = mc.get_value(key);
461
+ lines.push(`### ${key}`);
462
+ const entryType = attributes?.type || "text";
463
+ lines.push(`- **Type**: \`${entryType}\``);
464
+ lines.push(`- **System Tags**: \`${attributes?.systemTags?.join(", ") || "none"}\``);
465
+ lines.push(`- **Z-Index**: \`${attributes?.zIndex ?? 0}\``);
466
+ if (attributes?.contentTags && attributes.contentTags.length > 0) {
467
+ lines.push(`- **Tags**: \`${attributes.contentTags.join("`, `")}\``);
468
+ }
469
+ if (attributes?.contentType) {
470
+ lines.push(`- **Content Type**: \`${attributes.contentType}\``);
471
+ }
472
+ if (entryType === "image" || entryType === "file") {
473
+ const label = String.fromCharCode(65 + appendixCounter);
474
+ appendixCounter++;
475
+ lines.push(`- **Value**: [See Appendix ${label}]`);
476
+ appendixEntries.push({
477
+ key,
478
+ type: entryType,
479
+ contentType: attributes?.contentType || "application/octet-stream",
480
+ base64: value,
481
+ label
482
+ });
483
+ } else if (entryType === "json") {
484
+ lines.push("- **Value**:");
485
+ lines.push("```json");
486
+ try {
487
+ const jsonValue = typeof value === "string" ? value : JSON.stringify(value, null, 2);
488
+ lines.push(jsonValue);
489
+ } catch {
490
+ lines.push(String(value));
454
491
  }
492
+ lines.push("```");
493
+ } else {
494
+ lines.push("- **Value**:");
495
+ lines.push("```");
496
+ lines.push(String(value));
497
+ lines.push("```");
455
498
  }
499
+ lines.push("");
500
+ });
501
+ if (appendixEntries.length > 0) {
502
+ lines.push("---");
503
+ lines.push("");
504
+ lines.push("## Appendix: Binary Data");
505
+ lines.push("");
506
+ appendixEntries.forEach((entry) => {
507
+ lines.push(`### Appendix ${entry.label}: ${entry.key}`);
508
+ lines.push(`- **Type**: \`${entry.type}\``);
509
+ lines.push(`- **Content Type**: \`${entry.contentType}\``);
510
+ lines.push("- **Base64 Data**:");
511
+ lines.push("```");
512
+ lines.push(entry.base64);
513
+ lines.push("```");
514
+ lines.push("");
515
+ });
456
516
  }
457
- return normalized;
517
+ return lines.join("\n");
458
518
  }
459
- // Cloud sync state
460
- _cloudAdapter = null;
461
- _connectionState = "disconnected";
462
- _isLoaded = true;
463
- // Default true for local mode
464
- _cloudConfig = null;
465
- // Access level for system operations
466
- _accessLevel = "user";
467
- _initPromise = null;
468
- // Y-IndexedDB provider
469
- _idbProvider = null;
470
- // Undo Managers Cache
471
- _undoManagers = /* @__PURE__ */ new Map();
472
- // Global Undo Manager (watches entire rootMap)
473
- _globalUndoManager = null;
474
- // History tracking
475
- _history = [];
476
- _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
477
- _historyEnabled = false;
478
- constructor(options) {
479
- this.doc = new Y.Doc();
480
- this.rootMap = this.doc.getMap("mindcache");
481
- this.rootMap.observeDeep((events) => {
482
- const keysAffected = /* @__PURE__ */ new Set();
483
- events.forEach((event) => {
484
- if (event.target === this.rootMap) {
485
- const mapEvent = event;
486
- mapEvent.keysChanged.forEach((key) => keysAffected.add(key));
487
- } else if (event.target.parent === this.rootMap) {
488
- for (const [key, val] of this.rootMap) {
489
- if (val === event.target) {
490
- keysAffected.add(key);
491
- break;
492
- }
519
+ /**
520
+ * Import Markdown into MindCache data.
521
+ * @param markdown The markdown string to import
522
+ * @param mc The MindCache instance to import into
523
+ * @param merge If false (default), clears existing data before importing
524
+ */
525
+ static fromMarkdown(markdown, mc, merge = false) {
526
+ const lines = markdown.split("\n");
527
+ let currentKey = null;
528
+ let currentAttributes = {};
529
+ let currentValue = null;
530
+ let inCodeBlock = false;
531
+ let codeBlockContent = [];
532
+ if (!merge) {
533
+ mc.clear();
534
+ }
535
+ for (const line of lines) {
536
+ if (line.startsWith("### ") && !line.startsWith("### Appendix")) {
537
+ if (currentKey && currentValue !== null) {
538
+ mc.set_value(currentKey, currentValue.trim(), currentAttributes);
539
+ }
540
+ currentKey = line.substring(4).trim();
541
+ currentAttributes = {};
542
+ currentValue = null;
543
+ continue;
544
+ }
545
+ if (line.startsWith("### Appendix ")) {
546
+ if (currentKey && currentValue !== null) {
547
+ mc.set_value(currentKey, currentValue.trim(), currentAttributes);
548
+ }
549
+ const match = line.match(/### Appendix ([A-Z]): (.+)/);
550
+ if (match) {
551
+ currentKey = match[2];
552
+ currentAttributes = {};
553
+ currentValue = null;
554
+ }
555
+ continue;
556
+ }
557
+ if (line.startsWith("- **Type**:")) {
558
+ const type = line.match(/`(.+)`/)?.[1];
559
+ if (type) {
560
+ currentAttributes.type = type;
561
+ }
562
+ continue;
563
+ }
564
+ if (line.startsWith("- **System Tags**:")) {
565
+ const tagsStr = line.match(/`([^`]+)`/)?.[1] || "";
566
+ if (tagsStr !== "none") {
567
+ currentAttributes.systemTags = tagsStr.split(", ").filter((t) => t);
568
+ }
569
+ continue;
570
+ }
571
+ if (line.startsWith("- **Z-Index**:")) {
572
+ const zIndex = parseInt(line.match(/`(\d+)`/)?.[1] || "0", 10);
573
+ currentAttributes.zIndex = zIndex;
574
+ continue;
575
+ }
576
+ if (line.startsWith("- **Tags**:")) {
577
+ const tags = line.match(/`([^`]+)`/g)?.map((t) => t.slice(1, -1)) || [];
578
+ currentAttributes.contentTags = tags;
579
+ continue;
580
+ }
581
+ if (line.startsWith("- **Content Type**:")) {
582
+ currentAttributes.contentType = line.match(/`(.+)`/)?.[1];
583
+ continue;
584
+ }
585
+ if (line.startsWith("- **Base64 Data**:")) {
586
+ currentValue = "";
587
+ continue;
588
+ }
589
+ if (line.startsWith("- **Value**:")) {
590
+ const afterValue = line.substring(12).trim();
591
+ if (afterValue.includes("[See Appendix")) {
592
+ if (currentKey) {
593
+ mc.set_value(currentKey, "", currentAttributes);
493
594
  }
595
+ currentValue = null;
596
+ continue;
597
+ }
598
+ if (afterValue === "") {
599
+ currentValue = "";
600
+ } else if (afterValue === "```" || afterValue === "```json") {
601
+ inCodeBlock = true;
602
+ codeBlockContent = [];
603
+ currentValue = "";
604
+ } else if (afterValue.startsWith("```")) {
605
+ inCodeBlock = true;
606
+ codeBlockContent = [afterValue.substring(3)];
607
+ currentValue = "";
494
608
  } else {
495
- let current = event.target;
496
- while (current && current.parent) {
497
- if (current.parent.parent === this.rootMap) {
498
- for (const [key, val] of this.rootMap) {
499
- if (val === current.parent) {
500
- keysAffected.add(key);
501
- break;
502
- }
503
- }
504
- break;
505
- }
506
- current = current.parent;
507
- }
609
+ currentValue = afterValue;
508
610
  }
509
- });
510
- keysAffected.forEach((key) => {
511
- const entryMap = this.rootMap.get(key);
512
- if (entryMap) {
513
- const value = entryMap.get("value");
514
- const attrs = entryMap.get("attributes");
515
- const resolvedValue = attrs?.type === "document" && value instanceof Y.Text ? value.toString() : value;
516
- if (this.listeners[key]) {
517
- this.listeners[key].forEach((l) => l(resolvedValue));
611
+ continue;
612
+ }
613
+ const trimmedLine = line.trim();
614
+ if (trimmedLine === "```json" || trimmedLine === "```") {
615
+ if (inCodeBlock) {
616
+ inCodeBlock = false;
617
+ if (currentKey && codeBlockContent.length > 0) {
618
+ currentValue = codeBlockContent.join("\n");
518
619
  }
620
+ codeBlockContent = [];
519
621
  } else {
520
- if (this.listeners[key]) {
521
- this.listeners[key].forEach((l) => l(void 0));
522
- }
622
+ inCodeBlock = true;
623
+ codeBlockContent = [];
624
+ }
625
+ continue;
626
+ }
627
+ if (inCodeBlock) {
628
+ codeBlockContent.push(line);
629
+ } else if (currentKey && currentValue !== null) {
630
+ if (line.trim() === "---" || line.startsWith("## Appendix")) {
631
+ mc.set_value(currentKey, currentValue.trim(), currentAttributes);
632
+ currentKey = null;
633
+ currentValue = null;
634
+ currentAttributes = {};
635
+ } else {
636
+ currentValue += "\n" + line;
523
637
  }
524
- });
525
- this.notifyGlobalListeners();
526
- });
527
- this.initGlobalUndoManager();
528
- if (options?.accessLevel) {
529
- this._accessLevel = options.accessLevel;
530
- }
531
- const initPromises = [];
532
- if (options?.cloud) {
533
- this._cloudConfig = options.cloud;
534
- this._isLoaded = false;
535
- this._connectionState = "disconnected";
536
- initPromises.push(this._initCloud());
537
- if (typeof window !== "undefined") {
538
- const dbName = `mindcache_cloud_${options.cloud.instanceId}`;
539
- initPromises.push(this._initYIndexedDB(dbName));
540
638
  }
541
- this.enableHistory(options.history);
542
639
  }
543
- if (options?.indexedDB && !options?.cloud) {
544
- this._isLoaded = false;
545
- initPromises.push(this._initYIndexedDB(options.indexedDB.dbName || "mindcache_yjs_db"));
546
- this.enableHistory(options.history);
640
+ if (currentKey && currentValue !== null) {
641
+ mc.set_value(currentKey, currentValue.trim(), currentAttributes);
547
642
  }
548
- if (initPromises.length > 0) {
549
- this._initPromise = Promise.all(initPromises).then(() => {
550
- if (!this._cloudConfig) {
551
- this._isLoaded = true;
552
- }
643
+ const hasParsedKeys = lines.some((line) => line.startsWith("### ") && !line.startsWith("### Appendix"));
644
+ const isSTMExport = markdown.includes("# MindCache STM Export") || markdown.includes("## STM Entries");
645
+ if (!hasParsedKeys && !isSTMExport && markdown.trim().length > 0) {
646
+ mc.set_value("imported_content", markdown.trim(), {
647
+ type: "text",
648
+ systemTags: ["SystemPrompt", "LLMWrite"],
649
+ // Default assumptions
650
+ zIndex: 0
553
651
  });
554
652
  }
555
653
  }
556
- // Helper: Get or Create UndoManager for a key
557
- getUndoManager(key) {
558
- const entryMap = this.rootMap.get(key);
559
- if (!entryMap) {
560
- return void 0;
654
+ /**
655
+ * Parse markdown and return STM data without applying to a MindCache instance.
656
+ * Useful for validation or preview.
657
+ */
658
+ static parseMarkdown(markdown) {
659
+ const result = {};
660
+ const lines = markdown.split("\n");
661
+ let currentKey = null;
662
+ let currentAttributes = {};
663
+ let currentValue = null;
664
+ let inCodeBlock = false;
665
+ let codeBlockContent = [];
666
+ const saveEntry = () => {
667
+ if (currentKey && currentValue !== null) {
668
+ result[currentKey] = {
669
+ value: currentValue.trim(),
670
+ attributes: { ...DEFAULT_KEY_ATTRIBUTES, ...currentAttributes }
671
+ };
672
+ }
673
+ };
674
+ for (const line of lines) {
675
+ if (line.startsWith("### ") && !line.startsWith("### Appendix")) {
676
+ saveEntry();
677
+ currentKey = line.substring(4).trim();
678
+ currentAttributes = {};
679
+ currentValue = null;
680
+ continue;
681
+ }
682
+ if (line.startsWith("### Appendix ")) {
683
+ saveEntry();
684
+ const match = line.match(/### Appendix ([A-Z]): (.+)/);
685
+ if (match) {
686
+ currentKey = match[2];
687
+ currentAttributes = {};
688
+ currentValue = null;
689
+ }
690
+ continue;
691
+ }
692
+ if (line.startsWith("- **Type**:")) {
693
+ const type = line.match(/`(.+)`/)?.[1];
694
+ if (type) {
695
+ currentAttributes.type = type;
696
+ }
697
+ continue;
698
+ }
699
+ if (line.startsWith("- **System Tags**:")) {
700
+ const tagsStr = line.match(/`([^`]+)`/)?.[1] || "";
701
+ if (tagsStr !== "none") {
702
+ currentAttributes.systemTags = tagsStr.split(", ").filter((t) => t);
703
+ }
704
+ continue;
705
+ }
706
+ if (line.startsWith("- **Z-Index**:")) {
707
+ const zIndex = parseInt(line.match(/`(\d+)`/)?.[1] || "0", 10);
708
+ currentAttributes.zIndex = zIndex;
709
+ continue;
710
+ }
711
+ if (line.startsWith("- **Tags**:")) {
712
+ const tags = line.match(/`([^`]+)`/g)?.map((t) => t.slice(1, -1)) || [];
713
+ currentAttributes.contentTags = tags;
714
+ continue;
715
+ }
716
+ if (line.startsWith("- **Content Type**:")) {
717
+ currentAttributes.contentType = line.match(/`(.+)`/)?.[1];
718
+ continue;
719
+ }
720
+ if (line.startsWith("- **Base64 Data**:")) {
721
+ currentValue = "";
722
+ continue;
723
+ }
724
+ if (line.startsWith("- **Value**:")) {
725
+ const afterValue = line.substring(12).trim();
726
+ if (afterValue.includes("[See Appendix")) {
727
+ currentValue = "";
728
+ continue;
729
+ }
730
+ if (afterValue === "" || afterValue === "```" || afterValue === "```json") {
731
+ inCodeBlock = afterValue !== "";
732
+ codeBlockContent = [];
733
+ currentValue = "";
734
+ } else if (afterValue.startsWith("```")) {
735
+ inCodeBlock = true;
736
+ codeBlockContent = [afterValue.substring(3)];
737
+ currentValue = "";
738
+ } else {
739
+ currentValue = afterValue;
740
+ }
741
+ continue;
742
+ }
743
+ const trimmedLine = line.trim();
744
+ if (trimmedLine === "```json" || trimmedLine === "```") {
745
+ if (inCodeBlock) {
746
+ inCodeBlock = false;
747
+ if (currentKey && codeBlockContent.length > 0) {
748
+ currentValue = codeBlockContent.join("\n");
749
+ }
750
+ codeBlockContent = [];
751
+ } else {
752
+ inCodeBlock = true;
753
+ codeBlockContent = [];
754
+ }
755
+ continue;
756
+ }
757
+ if (inCodeBlock) {
758
+ codeBlockContent.push(line);
759
+ } else if (currentKey && currentValue !== null) {
760
+ if (line.trim() === "---" || line.startsWith("## Appendix")) {
761
+ saveEntry();
762
+ currentKey = null;
763
+ currentValue = null;
764
+ currentAttributes = {};
765
+ } else {
766
+ currentValue += "\n" + line;
767
+ }
768
+ }
769
+ }
770
+ saveEntry();
771
+ const hasParsedKeys = lines.some((line) => line.startsWith("### ") && !line.startsWith("### Appendix"));
772
+ const isSTMExport = markdown.includes("# MindCache STM Export") || markdown.includes("## STM Entries");
773
+ if (!hasParsedKeys && !isSTMExport && markdown.trim().length > 0) {
774
+ result["imported_content"] = {
775
+ value: markdown.trim(),
776
+ attributes: {
777
+ ...DEFAULT_KEY_ATTRIBUTES,
778
+ systemTags: ["SystemPrompt", "LLMWrite"]
779
+ }
780
+ };
781
+ }
782
+ return result;
783
+ }
784
+ };
785
+
786
+ // src/core/AIToolBuilder.ts
787
+ var AIToolBuilder = class _AIToolBuilder {
788
+ /**
789
+ * Sanitize key name for use in tool names
790
+ */
791
+ static sanitizeKeyForTool(key) {
792
+ return key.replace(/[^a-zA-Z0-9_-]/g, "_");
793
+ }
794
+ /**
795
+ * Find original key from sanitized tool name
796
+ */
797
+ static findKeyFromSanitizedTool(mc, sanitizedKey) {
798
+ for (const key of mc.keys()) {
799
+ if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
800
+ return key;
801
+ }
802
+ }
803
+ return void 0;
804
+ }
805
+ /**
806
+ * Generate Vercel AI SDK compatible tools for writable keys.
807
+ * For document type keys, generates additional tools: append_, insert_, edit_
808
+ *
809
+ * Security: All tools use llm_set_key internally which:
810
+ * - Only modifies VALUES, never attributes/systemTags
811
+ * - Prevents LLMs from escalating privileges
812
+ */
813
+ static createVercelAITools(mc) {
814
+ const tools = {};
815
+ for (const key of mc.keys()) {
816
+ if (key.startsWith("$")) {
817
+ continue;
818
+ }
819
+ if (!mc.keyMatchesContext(key)) {
820
+ continue;
821
+ }
822
+ const attributes = mc.get_attributes(key);
823
+ const isWritable = attributes?.systemTags?.includes("LLMWrite");
824
+ if (!isWritable) {
825
+ continue;
826
+ }
827
+ const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
828
+ const isDocument = attributes?.type === "document";
829
+ tools[`write_${sanitizedKey}`] = {
830
+ description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
831
+ inputSchema: {
832
+ type: "object",
833
+ properties: {
834
+ value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
835
+ },
836
+ required: ["value"]
837
+ },
838
+ execute: async ({ value }) => {
839
+ const success = mc.llm_set_key(key, value);
840
+ if (success) {
841
+ return {
842
+ result: `Successfully wrote "${value}" to ${key}`,
843
+ key,
844
+ value
845
+ };
846
+ }
847
+ return {
848
+ result: `Failed to write to ${key} - permission denied or key not found`,
849
+ key,
850
+ error: true
851
+ };
852
+ }
853
+ };
854
+ if (isDocument) {
855
+ tools[`append_${sanitizedKey}`] = {
856
+ description: `Append text to the end of "${key}" document`,
857
+ inputSchema: {
858
+ type: "object",
859
+ properties: {
860
+ text: { type: "string", description: "Text to append" }
861
+ },
862
+ required: ["text"]
863
+ },
864
+ execute: async ({ text }) => {
865
+ if (!attributes?.systemTags?.includes("LLMWrite")) {
866
+ return { result: `Permission denied for ${key}`, key, error: true };
867
+ }
868
+ const yText = mc.get_document(key);
869
+ if (yText) {
870
+ yText.insert(yText.length, text);
871
+ return {
872
+ result: `Successfully appended to ${key}`,
873
+ key,
874
+ appended: text
875
+ };
876
+ }
877
+ return { result: `Document ${key} not found`, key };
878
+ }
879
+ };
880
+ tools[`insert_${sanitizedKey}`] = {
881
+ description: `Insert text at a position in "${key}" document`,
882
+ inputSchema: {
883
+ type: "object",
884
+ properties: {
885
+ index: { type: "number", description: "Position to insert at (0 = start)" },
886
+ text: { type: "string", description: "Text to insert" }
887
+ },
888
+ required: ["index", "text"]
889
+ },
890
+ execute: async ({ index, text }) => {
891
+ if (!attributes?.systemTags?.includes("LLMWrite")) {
892
+ return { result: `Permission denied for ${key}`, key, error: true };
893
+ }
894
+ mc.insert_text(key, index, text);
895
+ return {
896
+ result: `Successfully inserted text at position ${index} in ${key}`,
897
+ key,
898
+ index,
899
+ inserted: text
900
+ };
901
+ }
902
+ };
903
+ tools[`edit_${sanitizedKey}`] = {
904
+ description: `Find and replace text in "${key}" document`,
905
+ inputSchema: {
906
+ type: "object",
907
+ properties: {
908
+ find: { type: "string", description: "Text to find" },
909
+ replace: { type: "string", description: "Replacement text" }
910
+ },
911
+ required: ["find", "replace"]
912
+ },
913
+ execute: async ({ find, replace }) => {
914
+ if (!attributes?.systemTags?.includes("LLMWrite")) {
915
+ return { result: `Permission denied for ${key}`, key, error: true };
916
+ }
917
+ const yText = mc.get_document(key);
918
+ if (yText) {
919
+ const text = yText.toString();
920
+ const idx = text.indexOf(find);
921
+ if (idx !== -1) {
922
+ yText.delete(idx, find.length);
923
+ yText.insert(idx, replace);
924
+ return {
925
+ result: `Successfully replaced "${find}" with "${replace}" in ${key}`,
926
+ key,
927
+ find,
928
+ replace,
929
+ index: idx
930
+ };
931
+ }
932
+ return { result: `Text "${find}" not found in ${key}`, key };
933
+ }
934
+ return { result: `Document ${key} not found`, key };
935
+ }
936
+ };
937
+ }
938
+ }
939
+ return tools;
940
+ }
941
+ /**
942
+ * Generate a system prompt containing all visible STM keys and their values.
943
+ * Indicates which tools can be used to modify writable keys.
944
+ */
945
+ static getSystemPrompt(mc) {
946
+ const lines = [];
947
+ for (const key of mc.keys()) {
948
+ if (key.startsWith("$")) {
949
+ continue;
950
+ }
951
+ if (!mc.keyMatchesContext(key)) {
952
+ continue;
953
+ }
954
+ const attributes = mc.get_attributes(key);
955
+ const isVisible = attributes?.systemTags?.includes("SystemPrompt") || attributes?.systemTags?.includes("LLMRead");
956
+ if (!isVisible) {
957
+ continue;
958
+ }
959
+ const value = mc.get_value(key);
960
+ const displayValue = typeof value === "object" ? JSON.stringify(value) : value;
961
+ const isWritable = attributes?.systemTags?.includes("LLMWrite");
962
+ const isDocument = attributes?.type === "document";
963
+ const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
964
+ if (isWritable) {
965
+ if (isDocument) {
966
+ lines.push(
967
+ `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
968
+ );
969
+ } else {
970
+ const oldValueHint = displayValue ? ` This tool DOES NOT append \u2014 start your response with the old value (${displayValue})` : "";
971
+ lines.push(
972
+ `${key}: ${displayValue}. You can rewrite "${key}" by using the write_${sanitizedKey} tool.${oldValueHint}`
973
+ );
974
+ }
975
+ } else {
976
+ lines.push(`${key}: ${displayValue}`);
977
+ }
978
+ }
979
+ return lines.join("\n");
980
+ }
981
+ /**
982
+ * Execute a tool call by name with the given value.
983
+ * Returns the result or null if tool not found.
984
+ */
985
+ static executeToolCall(mc, toolName, value) {
986
+ const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
987
+ if (!match) {
988
+ return null;
989
+ }
990
+ const [, action, sanitizedKey] = match;
991
+ const key = _AIToolBuilder.findKeyFromSanitizedTool(mc, sanitizedKey);
992
+ if (!key) {
993
+ return null;
994
+ }
995
+ const attributes = mc.get_attributes(key);
996
+ if (!attributes) {
997
+ return null;
998
+ }
999
+ const isWritable = attributes?.systemTags?.includes("LLMWrite");
1000
+ if (!isWritable) {
1001
+ return null;
1002
+ }
1003
+ const isDocument = attributes?.type === "document";
1004
+ switch (action) {
1005
+ case "write":
1006
+ if (isDocument) {
1007
+ mc._replaceDocumentText(key, value);
1008
+ } else {
1009
+ mc.set_value(key, value);
1010
+ }
1011
+ return {
1012
+ result: `Successfully wrote "${value}" to ${key}`,
1013
+ key,
1014
+ value
1015
+ };
1016
+ case "append":
1017
+ if (isDocument) {
1018
+ const yText = mc.get_document(key);
1019
+ if (yText) {
1020
+ yText.insert(yText.length, value);
1021
+ return {
1022
+ result: `Successfully appended to ${key}`,
1023
+ key,
1024
+ value
1025
+ };
1026
+ }
1027
+ }
1028
+ return null;
1029
+ case "insert":
1030
+ if (isDocument && typeof value === "object" && value.index !== void 0 && value.text) {
1031
+ mc.insert_text(key, value.index, value.text);
1032
+ return {
1033
+ result: `Successfully inserted at position ${value.index} in ${key}`,
1034
+ key,
1035
+ value: value.text
1036
+ };
1037
+ }
1038
+ return null;
1039
+ case "edit":
1040
+ if (isDocument && typeof value === "object" && value.find && value.replace !== void 0) {
1041
+ const yText = mc.get_document(key);
1042
+ if (yText) {
1043
+ const text = yText.toString();
1044
+ const idx = text.indexOf(value.find);
1045
+ if (idx !== -1) {
1046
+ yText.delete(idx, value.find.length);
1047
+ yText.insert(idx, value.replace);
1048
+ return {
1049
+ result: `Successfully replaced "${value.find}" with "${value.replace}" in ${key}`,
1050
+ key,
1051
+ value: value.replace
1052
+ };
1053
+ }
1054
+ }
1055
+ }
1056
+ return null;
1057
+ default:
1058
+ return null;
1059
+ }
1060
+ }
1061
+ };
1062
+
1063
+ // src/core/TagManager.ts
1064
+ var TagManager = class _TagManager {
1065
+ // ============================================
1066
+ // Content Tag Methods
1067
+ // ============================================
1068
+ /**
1069
+ * Add a content tag to a key.
1070
+ * @returns true if the tag was added, false if key doesn't exist or tag already exists
1071
+ */
1072
+ static addTag(mc, key, tag) {
1073
+ const entryMap = mc.rootMap.get(key);
1074
+ if (!entryMap) {
1075
+ return false;
1076
+ }
1077
+ const attributes = entryMap.get("attributes");
1078
+ const contentTags = attributes?.contentTags || [];
1079
+ if (contentTags.includes(tag)) {
1080
+ return false;
1081
+ }
1082
+ mc.doc.transact(() => {
1083
+ const newContentTags = [...contentTags, tag];
1084
+ entryMap.set("attributes", {
1085
+ ...attributes,
1086
+ contentTags: newContentTags,
1087
+ tags: newContentTags
1088
+ // Sync legacy tags array
1089
+ });
1090
+ });
1091
+ mc.notifyGlobalListeners();
1092
+ return true;
1093
+ }
1094
+ /**
1095
+ * Remove a content tag from a key.
1096
+ * @returns true if the tag was removed
1097
+ */
1098
+ static removeTag(mc, key, tag) {
1099
+ const entryMap = mc.rootMap.get(key);
1100
+ if (!entryMap) {
1101
+ return false;
1102
+ }
1103
+ const attributes = entryMap.get("attributes");
1104
+ const contentTags = attributes?.contentTags || [];
1105
+ const tagIndex = contentTags.indexOf(tag);
1106
+ if (tagIndex === -1) {
1107
+ return false;
1108
+ }
1109
+ mc.doc.transact(() => {
1110
+ const newContentTags = contentTags.filter((t) => t !== tag);
1111
+ entryMap.set("attributes", {
1112
+ ...attributes,
1113
+ contentTags: newContentTags,
1114
+ tags: newContentTags
1115
+ // Sync legacy tags array
1116
+ });
1117
+ });
1118
+ mc.notifyGlobalListeners();
1119
+ return true;
1120
+ }
1121
+ /**
1122
+ * Get all content tags for a key.
1123
+ */
1124
+ static getTags(mc, key) {
1125
+ const entryMap = mc.rootMap.get(key);
1126
+ if (!entryMap) {
1127
+ return [];
1128
+ }
1129
+ const attributes = entryMap.get("attributes");
1130
+ return attributes?.contentTags || [];
1131
+ }
1132
+ /**
1133
+ * Get all unique content tags across all keys.
1134
+ */
1135
+ static getAllTags(mc) {
1136
+ const allTags = /* @__PURE__ */ new Set();
1137
+ for (const [, val] of mc.rootMap) {
1138
+ const entryMap = val;
1139
+ const attributes = entryMap.get("attributes");
1140
+ if (attributes?.contentTags) {
1141
+ attributes.contentTags.forEach((tag) => allTags.add(tag));
1142
+ }
1143
+ }
1144
+ return Array.from(allTags);
1145
+ }
1146
+ /**
1147
+ * Check if a key has a specific content tag.
1148
+ */
1149
+ static hasTag(mc, key, tag) {
1150
+ const entryMap = mc.rootMap.get(key);
1151
+ if (!entryMap) {
1152
+ return false;
1153
+ }
1154
+ const attributes = entryMap.get("attributes");
1155
+ return attributes?.contentTags?.includes(tag) || false;
1156
+ }
1157
+ /**
1158
+ * Get all keys with a specific content tag as formatted string.
1159
+ */
1160
+ static getTagged(mc, tag) {
1161
+ const entries = [];
1162
+ const keys = mc.getSortedKeys();
1163
+ keys.forEach((key) => {
1164
+ if (_TagManager.hasTag(mc, key, tag)) {
1165
+ entries.push([key, mc.get_value(key)]);
1166
+ }
1167
+ });
1168
+ return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
1169
+ }
1170
+ /**
1171
+ * Get array of keys with a specific content tag.
1172
+ */
1173
+ static getKeysByTag(mc, tag) {
1174
+ const keys = mc.getSortedKeys();
1175
+ return keys.filter((key) => _TagManager.hasTag(mc, key, tag));
1176
+ }
1177
+ // ============================================
1178
+ // System Tag Methods (requires system access)
1179
+ // ============================================
1180
+ /**
1181
+ * Add a system tag to a key (requires system access).
1182
+ */
1183
+ static systemAddTag(mc, key, tag) {
1184
+ if (!mc.hasSystemAccess) {
1185
+ console.warn("MindCache: systemAddTag requires system access level");
1186
+ return false;
1187
+ }
1188
+ const entryMap = mc.rootMap.get(key);
1189
+ if (!entryMap) {
1190
+ return false;
1191
+ }
1192
+ const attributes = entryMap.get("attributes");
1193
+ const systemTags = attributes?.systemTags || [];
1194
+ if (systemTags.includes(tag)) {
1195
+ return false;
1196
+ }
1197
+ mc.doc.transact(() => {
1198
+ const newSystemTags = [...systemTags, tag];
1199
+ const normalizedTags = mc.normalizeSystemTags(newSystemTags);
1200
+ entryMap.set("attributes", {
1201
+ ...attributes,
1202
+ systemTags: normalizedTags
1203
+ });
1204
+ });
1205
+ mc.notifyGlobalListeners();
1206
+ return true;
1207
+ }
1208
+ /**
1209
+ * Remove a system tag from a key (requires system access).
1210
+ */
1211
+ static systemRemoveTag(mc, key, tag) {
1212
+ if (!mc.hasSystemAccess) {
1213
+ console.warn("MindCache: systemRemoveTag requires system access level");
1214
+ return false;
1215
+ }
1216
+ const entryMap = mc.rootMap.get(key);
1217
+ if (!entryMap) {
1218
+ return false;
1219
+ }
1220
+ const attributes = entryMap.get("attributes");
1221
+ const systemTags = attributes?.systemTags || [];
1222
+ const tagIndex = systemTags.indexOf(tag);
1223
+ if (tagIndex === -1) {
1224
+ return false;
1225
+ }
1226
+ mc.doc.transact(() => {
1227
+ const newSystemTags = systemTags.filter((t) => t !== tag);
1228
+ entryMap.set("attributes", {
1229
+ ...attributes,
1230
+ systemTags: newSystemTags
1231
+ });
1232
+ });
1233
+ mc.notifyGlobalListeners();
1234
+ return true;
1235
+ }
1236
+ /**
1237
+ * Get all system tags for a key (requires system access).
1238
+ */
1239
+ static systemGetTags(mc, key) {
1240
+ if (!mc.hasSystemAccess) {
1241
+ console.warn("MindCache: systemGetTags requires system access level");
1242
+ return [];
1243
+ }
1244
+ const entryMap = mc.rootMap.get(key);
1245
+ if (!entryMap) {
1246
+ return [];
1247
+ }
1248
+ const attributes = entryMap.get("attributes");
1249
+ return attributes?.systemTags || [];
1250
+ }
1251
+ /**
1252
+ * Check if a key has a specific system tag (requires system access).
1253
+ */
1254
+ static systemHasTag(mc, key, tag) {
1255
+ if (!mc.hasSystemAccess) {
1256
+ console.warn("MindCache: systemHasTag requires system access level");
1257
+ return false;
1258
+ }
1259
+ const entryMap = mc.rootMap.get(key);
1260
+ if (!entryMap) {
1261
+ return false;
1262
+ }
1263
+ const attributes = entryMap.get("attributes");
1264
+ return attributes?.systemTags?.includes(tag) || false;
1265
+ }
1266
+ /**
1267
+ * Set all system tags for a key at once (requires system access).
1268
+ */
1269
+ static systemSetTags(mc, key, tags) {
1270
+ if (!mc.hasSystemAccess) {
1271
+ console.warn("MindCache: systemSetTags requires system access level");
1272
+ return false;
1273
+ }
1274
+ const entryMap = mc.rootMap.get(key);
1275
+ if (!entryMap) {
1276
+ return false;
1277
+ }
1278
+ mc.doc.transact(() => {
1279
+ const attributes = entryMap.get("attributes");
1280
+ entryMap.set("attributes", {
1281
+ ...attributes,
1282
+ systemTags: [...tags]
1283
+ });
1284
+ });
1285
+ mc.notifyGlobalListeners();
1286
+ return true;
1287
+ }
1288
+ /**
1289
+ * Get all keys with a specific system tag (requires system access).
1290
+ */
1291
+ static systemGetKeysByTag(mc, tag) {
1292
+ if (!mc.hasSystemAccess) {
1293
+ console.warn("MindCache: systemGetKeysByTag requires system access level");
1294
+ return [];
1295
+ }
1296
+ const keys = mc.getSortedKeys();
1297
+ return keys.filter((key) => _TagManager.systemHasTag(mc, key, tag));
1298
+ }
1299
+ };
1300
+
1301
+ // src/core/MindCache.ts
1302
+ var MindCache = class {
1303
+ // Public doc for adapter access
1304
+ doc;
1305
+ rootMap;
1306
+ // Key -> EntryMap({value, attributes})
1307
+ // Cache listeners
1308
+ listeners = {};
1309
+ globalListeners = [];
1310
+ // Metadata
1311
+ version = "3.3.2";
1312
+ // Internal flag to prevent sync loops when receiving remote updates
1313
+ // (Less critical with Yjs but kept for API compat)
1314
+ normalizeSystemTags(tags) {
1315
+ const normalized = [];
1316
+ const seen = /* @__PURE__ */ new Set();
1317
+ for (const tag of tags) {
1318
+ if (["SystemPrompt", "LLMRead", "LLMWrite", "ApplyTemplate"].includes(tag)) {
1319
+ if (!seen.has(tag)) {
1320
+ seen.add(tag);
1321
+ normalized.push(tag);
1322
+ }
1323
+ }
1324
+ }
1325
+ return normalized;
1326
+ }
1327
+ // Cloud sync state
1328
+ _cloudAdapter = null;
1329
+ _connectionState = "disconnected";
1330
+ _isLoaded = true;
1331
+ // Default true for local mode
1332
+ _cloudConfig = null;
1333
+ // Access level for admin operations
1334
+ _accessLevel = "user";
1335
+ // Context filtering (client-local, not persisted)
1336
+ _contextRules = null;
1337
+ _initPromise = null;
1338
+ // Y-IndexedDB provider
1339
+ _idbProvider = null;
1340
+ // Undo Managers Cache
1341
+ _undoManagers = /* @__PURE__ */ new Map();
1342
+ // Global Undo Manager (watches entire rootMap)
1343
+ _globalUndoManager = null;
1344
+ // History tracking
1345
+ _history = [];
1346
+ _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
1347
+ _historyEnabled = false;
1348
+ constructor(options) {
1349
+ this.doc = options?.doc || new Y.Doc();
1350
+ this.rootMap = this.doc.getMap("mindcache");
1351
+ this.rootMap.observeDeep((events) => {
1352
+ const keysAffected = /* @__PURE__ */ new Set();
1353
+ events.forEach((event) => {
1354
+ if (event.target === this.rootMap) {
1355
+ const mapEvent = event;
1356
+ mapEvent.keysChanged.forEach((key) => keysAffected.add(key));
1357
+ } else if (event.target.parent === this.rootMap) {
1358
+ for (const [key, val] of this.rootMap) {
1359
+ if (val === event.target) {
1360
+ keysAffected.add(key);
1361
+ break;
1362
+ }
1363
+ }
1364
+ } else {
1365
+ let current = event.target;
1366
+ while (current && current.parent) {
1367
+ if (current.parent.parent === this.rootMap) {
1368
+ for (const [key, val] of this.rootMap) {
1369
+ if (val === current.parent) {
1370
+ keysAffected.add(key);
1371
+ break;
1372
+ }
1373
+ }
1374
+ break;
1375
+ }
1376
+ current = current.parent;
1377
+ }
1378
+ }
1379
+ });
1380
+ keysAffected.forEach((key) => {
1381
+ const entryMap = this.rootMap.get(key);
1382
+ if (entryMap) {
1383
+ const value = entryMap.get("value");
1384
+ const attrs = entryMap.get("attributes");
1385
+ const resolvedValue = attrs?.type === "document" && value instanceof Y.Text ? value.toString() : value;
1386
+ if (this.listeners[key]) {
1387
+ this.listeners[key].forEach((l) => l(resolvedValue));
1388
+ }
1389
+ } else {
1390
+ if (this.listeners[key]) {
1391
+ this.listeners[key].forEach((l) => l(void 0));
1392
+ }
1393
+ }
1394
+ });
1395
+ this.notifyGlobalListeners();
1396
+ });
1397
+ this.initGlobalUndoManager();
1398
+ if (options?.accessLevel) {
1399
+ this._accessLevel = options.accessLevel;
1400
+ }
1401
+ if (options?.context) {
1402
+ this._contextRules = options.context;
1403
+ }
1404
+ const initPromises = [];
1405
+ if (options?.cloud) {
1406
+ this._cloudConfig = options.cloud;
1407
+ this._isLoaded = false;
1408
+ this._connectionState = "disconnected";
1409
+ initPromises.push(this._initCloud());
1410
+ if (typeof window !== "undefined") {
1411
+ const dbName = `mindcache_cloud_${options.cloud.instanceId}`;
1412
+ initPromises.push(this._initYIndexedDB(dbName));
1413
+ }
1414
+ this.enableHistory(options.history);
1415
+ }
1416
+ if (options?.indexedDB && !options?.cloud) {
1417
+ this._isLoaded = false;
1418
+ initPromises.push(this._initYIndexedDB(options.indexedDB.dbName || "mindcache_yjs_db"));
1419
+ this.enableHistory(options.history);
1420
+ }
1421
+ if (initPromises.length > 0) {
1422
+ this._initPromise = Promise.all(initPromises).then(() => {
1423
+ if (!this._cloudConfig) {
1424
+ this._isLoaded = true;
1425
+ }
1426
+ });
1427
+ }
1428
+ }
1429
+ // Helper: Get or Create UndoManager for a key
1430
+ getUndoManager(key) {
1431
+ const entryMap = this.rootMap.get(key);
1432
+ if (!entryMap) {
1433
+ return void 0;
561
1434
  }
562
1435
  if (!this._undoManagers.has(key)) {
563
1436
  const um = new Y.UndoManager(entryMap, {
@@ -702,7 +1575,101 @@ var MindCache = class {
702
1575
  return this._accessLevel;
703
1576
  }
704
1577
  get hasSystemAccess() {
705
- return this._accessLevel === "system";
1578
+ return this._accessLevel === "admin";
1579
+ }
1580
+ // ============================================
1581
+ // Context Methods (client-local filtering)
1582
+ // ============================================
1583
+ /**
1584
+ * Check if context filtering is currently active.
1585
+ */
1586
+ get hasContext() {
1587
+ return this._contextRules !== null;
1588
+ }
1589
+ /**
1590
+ * Get current context rules, or null if no context is set.
1591
+ */
1592
+ get_context() {
1593
+ return this._contextRules;
1594
+ }
1595
+ /**
1596
+ * Set context filtering rules.
1597
+ * When context is set, only keys with ALL specified tags are visible.
1598
+ *
1599
+ * @param rules - Context rules, or array of tags (shorthand for { tags: [...] })
1600
+ */
1601
+ set_context(rules) {
1602
+ if (Array.isArray(rules)) {
1603
+ this._contextRules = { tags: rules };
1604
+ } else {
1605
+ this._contextRules = rules;
1606
+ }
1607
+ }
1608
+ /**
1609
+ * Clear context filtering. All keys become visible again.
1610
+ */
1611
+ reset_context() {
1612
+ this._contextRules = null;
1613
+ }
1614
+ /**
1615
+ * Check if a key matches the current context rules.
1616
+ * Returns true if no context is set.
1617
+ */
1618
+ keyMatchesContext(key) {
1619
+ if (key.startsWith("$")) {
1620
+ return true;
1621
+ }
1622
+ if (!this._contextRules) {
1623
+ return true;
1624
+ }
1625
+ if (this._contextRules.tags.length === 0) {
1626
+ return true;
1627
+ }
1628
+ const entryMap = this.rootMap.get(key);
1629
+ if (!entryMap) {
1630
+ return false;
1631
+ }
1632
+ const attrs = entryMap.get("attributes");
1633
+ const contentTags = attrs?.contentTags || [];
1634
+ return this._contextRules.tags.every((tag) => contentTags.includes(tag));
1635
+ }
1636
+ /**
1637
+ * Create a new key with optional default tags from context.
1638
+ *
1639
+ * @throws Error if key already exists
1640
+ */
1641
+ create_key(key, value, attributes) {
1642
+ if (this.rootMap.has(key)) {
1643
+ throw new Error(`Key already exists: ${key}. Use set_value to update.`);
1644
+ }
1645
+ let finalAttributes = { ...attributes };
1646
+ if (this._contextRules) {
1647
+ const contextContentTags = this._contextRules.defaultContentTags || this._contextRules.tags;
1648
+ const existingContentTags = finalAttributes.contentTags || [];
1649
+ finalAttributes.contentTags = [.../* @__PURE__ */ new Set([...existingContentTags, ...contextContentTags])];
1650
+ if (this._contextRules.defaultSystemTags) {
1651
+ const existingSystemTags = finalAttributes.systemTags || [];
1652
+ finalAttributes.systemTags = [.../* @__PURE__ */ new Set([...existingSystemTags, ...this._contextRules.defaultSystemTags])];
1653
+ }
1654
+ }
1655
+ const entryMap = new Y.Map();
1656
+ this.rootMap.set(key, entryMap);
1657
+ this.getUndoManager(key);
1658
+ this.doc.transact(() => {
1659
+ const baseAttributes = {
1660
+ ...DEFAULT_KEY_ATTRIBUTES,
1661
+ ...finalAttributes
1662
+ };
1663
+ if (baseAttributes.systemTags) {
1664
+ baseAttributes.systemTags = this.normalizeSystemTags(baseAttributes.systemTags);
1665
+ }
1666
+ let valueToSet = value;
1667
+ if (baseAttributes.type === "document" && !(valueToSet instanceof Y.Text)) {
1668
+ valueToSet = new Y.Text(typeof value === "string" ? value : String(value ?? ""));
1669
+ }
1670
+ entryMap.set("value", valueToSet);
1671
+ entryMap.set("attributes", baseAttributes);
1672
+ });
706
1673
  }
707
1674
  async _initCloud() {
708
1675
  if (!this._cloudConfig) {
@@ -877,10 +1844,11 @@ var MindCache = class {
877
1844
  this.rootMap.set(key, entryMap);
878
1845
  entryMap.set("value", entry.value);
879
1846
  const attrs = entry.attributes || {};
1847
+ const contentTags = attrs.contentTags || attrs.tags || [];
880
1848
  const normalizedAttrs = {
881
1849
  type: attrs.type || "text",
882
1850
  contentType: attrs.contentType,
883
- contentTags: attrs.contentTags || [],
1851
+ contentTags,
884
1852
  systemTags: this.normalizeSystemTags(attrs.systemTags || []),
885
1853
  zIndex: attrs.zIndex ?? 0
886
1854
  };
@@ -923,10 +1891,21 @@ var MindCache = class {
923
1891
  return false;
924
1892
  }
925
1893
  // InjectSTM replacement (private helper)
1894
+ // Handles special template variables: $date, $time, $version
926
1895
  _injectSTMInternal(template, _processingStack) {
927
1896
  return template.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
928
- const val = this.get_value(key.trim(), _processingStack);
929
- return val !== void 0 ? String(val) : `{{${key}}}`;
1897
+ const trimmedKey = key.trim();
1898
+ if (trimmedKey === "$date") {
1899
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1900
+ }
1901
+ if (trimmedKey === "$time") {
1902
+ return (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0];
1903
+ }
1904
+ if (trimmedKey === "$version") {
1905
+ return this.version;
1906
+ }
1907
+ const val = this.get_value(trimmedKey, _processingStack);
1908
+ return val !== void 0 ? String(val) : "";
930
1909
  });
931
1910
  }
932
1911
  /**
@@ -941,10 +1920,10 @@ var MindCache = class {
941
1920
  getAll() {
942
1921
  const result = {};
943
1922
  for (const [key] of this.rootMap) {
944
- result[key] = this.get_value(key);
1923
+ if (this.keyMatchesContext(key)) {
1924
+ result[key] = this.get_value(key);
1925
+ }
945
1926
  }
946
- result["$date"] = this.get_value("$date");
947
- result["$time"] = this.get_value("$time");
948
1927
  return result;
949
1928
  }
950
1929
  /**
@@ -955,30 +1934,24 @@ var MindCache = class {
955
1934
  getAllEntries() {
956
1935
  const result = {};
957
1936
  for (const [key] of this.rootMap) {
958
- const value = this.get_value(key);
959
- const attributes = this.get_attributes(key);
960
- if (attributes) {
961
- result[key] = { value, attributes };
1937
+ if (this.keyMatchesContext(key)) {
1938
+ const value = this.get_value(key);
1939
+ const attributes = this.get_attributes(key);
1940
+ if (attributes) {
1941
+ result[key] = { value, attributes };
1942
+ }
962
1943
  }
963
1944
  }
964
1945
  return result;
965
1946
  }
966
1947
  get_value(key, _processingStack) {
967
- if (key === "$date") {
968
- const today = /* @__PURE__ */ new Date();
969
- return today.toISOString().split("T")[0];
970
- }
971
- if (key === "$time") {
972
- const now = /* @__PURE__ */ new Date();
973
- return now.toTimeString().split(" ")[0];
974
- }
975
- if (key === "$version") {
976
- return this.version;
977
- }
978
1948
  const entryMap = this.rootMap.get(key);
979
1949
  if (!entryMap) {
980
1950
  return void 0;
981
1951
  }
1952
+ if (!this.keyMatchesContext(key)) {
1953
+ return void 0;
1954
+ }
982
1955
  const attributes = entryMap.get("attributes");
983
1956
  const value = entryMap.get("value");
984
1957
  if (attributes?.type === "document" && value instanceof Y.Text) {
@@ -997,28 +1970,21 @@ var MindCache = class {
997
1970
  return value;
998
1971
  }
999
1972
  get_attributes(key) {
1000
- if (key === "$date" || key === "$time" || key === "$version") {
1001
- return {
1002
- type: "text",
1003
- contentTags: [],
1004
- systemTags: ["SystemPrompt", "protected"],
1005
- zIndex: 999999
1006
- };
1007
- }
1008
1973
  const entryMap = this.rootMap.get(key);
1009
1974
  return entryMap ? entryMap.get("attributes") : void 0;
1010
1975
  }
1011
1976
  /**
1012
1977
  * Update only the attributes of a key without modifying the value.
1013
1978
  * Useful for updating tags, permissions etc. on document type keys.
1979
+ * @returns true if attributes were updated, false if key doesn't exist or is protected
1014
1980
  */
1015
1981
  set_attributes(key, attributes) {
1016
- if (key === "$date" || key === "$time" || key === "$version") {
1017
- return;
1018
- }
1019
1982
  const entryMap = this.rootMap.get(key);
1020
1983
  if (!entryMap) {
1021
- return;
1984
+ return false;
1985
+ }
1986
+ if (this._contextRules && !this.keyMatchesContext(key)) {
1987
+ throw new Error(`Cannot modify key "${key}": does not match current context`);
1022
1988
  }
1023
1989
  this.doc.transact(() => {
1024
1990
  const existingAttrs = entryMap.get("attributes");
@@ -1036,13 +2002,14 @@ var MindCache = class {
1036
2002
  entryMap.set("value", currentValue.toString());
1037
2003
  }
1038
2004
  });
2005
+ return true;
1039
2006
  }
1040
2007
  set_value(key, value, attributes) {
1041
- if (key === "$date" || key === "$time" || key === "$version") {
1042
- return;
1043
- }
1044
2008
  const existingEntry = this.rootMap.get(key);
1045
2009
  if (existingEntry) {
2010
+ if (this._contextRules && !this.keyMatchesContext(key)) {
2011
+ throw new Error(`Cannot modify key "${key}": does not match current context`);
2012
+ }
1046
2013
  const existingAttrs = existingEntry.get("attributes");
1047
2014
  if (existingAttrs?.type === "document") {
1048
2015
  if (!attributes?.type || attributes.type === "document") {
@@ -1056,6 +2023,10 @@ var MindCache = class {
1056
2023
  }
1057
2024
  }
1058
2025
  }
2026
+ if (!existingEntry && this._contextRules) {
2027
+ this.create_key(key, value, attributes);
2028
+ return;
2029
+ }
1059
2030
  if (!existingEntry && attributes?.type === "document") {
1060
2031
  this.set_document(key, typeof value === "string" ? value : "", attributes);
1061
2032
  return;
@@ -1096,9 +2067,6 @@ var MindCache = class {
1096
2067
  * Used by create_vercel_ai_tools() to prevent LLMs from escalating privileges.
1097
2068
  */
1098
2069
  llm_set_key(key, value) {
1099
- if (key === "$date" || key === "$time" || key === "$version") {
1100
- return false;
1101
- }
1102
2070
  const entryMap = this.rootMap.get(key);
1103
2071
  if (!entryMap) {
1104
2072
  return false;
@@ -1119,9 +2087,6 @@ var MindCache = class {
1119
2087
  return true;
1120
2088
  }
1121
2089
  delete_key(key) {
1122
- if (key === "$date" || key === "$time") {
1123
- return;
1124
- }
1125
2090
  this.rootMap.delete(key);
1126
2091
  }
1127
2092
  clear() {
@@ -1137,19 +2102,16 @@ var MindCache = class {
1137
2102
  * Check if a key exists in MindCache.
1138
2103
  */
1139
2104
  has(key) {
1140
- if (key === "$date" || key === "$time" || key === "$version") {
1141
- return true;
2105
+ if (!this.rootMap.has(key)) {
2106
+ return false;
1142
2107
  }
1143
- return this.rootMap.has(key);
2108
+ return this.keyMatchesContext(key);
1144
2109
  }
1145
2110
  /**
1146
2111
  * Delete a key from MindCache.
1147
2112
  * @returns true if the key existed and was deleted
1148
2113
  */
1149
2114
  delete(key) {
1150
- if (key === "$date" || key === "$time" || key === "$version") {
1151
- return false;
1152
- }
1153
2115
  if (!this.rootMap.has(key)) {
1154
2116
  return false;
1155
2117
  }
@@ -1175,9 +2137,7 @@ var MindCache = class {
1175
2137
  update(data) {
1176
2138
  this.doc.transact(() => {
1177
2139
  for (const [key, value] of Object.entries(data)) {
1178
- if (key !== "$date" && key !== "$time" && key !== "$version") {
1179
- this.set_value(key, value);
1180
- }
2140
+ this.set_value(key, value);
1181
2141
  }
1182
2142
  });
1183
2143
  this.notifyGlobalListeners();
@@ -1186,38 +2146,48 @@ var MindCache = class {
1186
2146
  * Get the number of keys in MindCache.
1187
2147
  */
1188
2148
  size() {
1189
- return this.rootMap.size + 2;
2149
+ let count = 0;
2150
+ for (const [key] of this.rootMap) {
2151
+ if (this.keyMatchesContext(key)) {
2152
+ count++;
2153
+ }
2154
+ }
2155
+ return count;
1190
2156
  }
1191
2157
  /**
1192
- * Get all keys in MindCache (including temporal keys).
2158
+ * Get all keys in MindCache.
1193
2159
  */
1194
2160
  keys() {
1195
- const keys = Array.from(this.rootMap.keys());
1196
- keys.push("$date", "$time");
2161
+ const keys = [];
2162
+ for (const [key] of this.rootMap) {
2163
+ if (this.keyMatchesContext(key)) {
2164
+ keys.push(key);
2165
+ }
2166
+ }
1197
2167
  return keys;
1198
2168
  }
1199
2169
  /**
1200
- * Get all values in MindCache (including temporal values).
2170
+ * Get all values in MindCache.
1201
2171
  */
1202
2172
  values() {
1203
2173
  const result = [];
1204
2174
  for (const [key] of this.rootMap) {
1205
- result.push(this.get_value(key));
2175
+ if (this.keyMatchesContext(key)) {
2176
+ result.push(this.get_value(key));
2177
+ }
1206
2178
  }
1207
- result.push(this.get_value("$date"));
1208
- result.push(this.get_value("$time"));
1209
2179
  return result;
1210
2180
  }
1211
2181
  /**
1212
- * Get all key-value entries (including temporal entries).
2182
+ * Get all key-value entries.
1213
2183
  */
1214
2184
  entries() {
1215
2185
  const result = [];
1216
2186
  for (const [key] of this.rootMap) {
1217
- result.push([key, this.get_value(key)]);
2187
+ if (this.keyMatchesContext(key)) {
2188
+ result.push([key, this.get_value(key)]);
2189
+ }
1218
2190
  }
1219
- result.push(["$date", this.get_value("$date")]);
1220
- result.push(["$time", this.get_value("$time")]);
1221
2191
  return result;
1222
2192
  }
1223
2193
  /**
@@ -1238,7 +2208,6 @@ var MindCache = class {
1238
2208
  }
1239
2209
  /**
1240
2210
  * Get the STM as an object with values directly (no attributes).
1241
- * Includes system keys ($date, $time).
1242
2211
  * @deprecated Use getAll() for full STM format
1243
2212
  */
1244
2213
  getSTMObject() {
@@ -1246,8 +2215,6 @@ var MindCache = class {
1246
2215
  for (const [key] of this.rootMap) {
1247
2216
  result[key] = this.get_value(key);
1248
2217
  }
1249
- result["$date"] = this.get_value("$date");
1250
- result["$time"] = this.get_value("$time");
1251
2218
  return result;
1252
2219
  }
1253
2220
  /**
@@ -1255,450 +2222,132 @@ var MindCache = class {
1255
2222
  * @returns true if the tag was added, false if key doesn't exist or tag already exists
1256
2223
  */
1257
2224
  addTag(key, tag) {
1258
- if (key === "$date" || key === "$time" || key === "$version") {
1259
- return false;
1260
- }
1261
- const entryMap = this.rootMap.get(key);
1262
- if (!entryMap) {
1263
- return false;
1264
- }
1265
- const attributes = entryMap.get("attributes");
1266
- const contentTags = attributes?.contentTags || [];
1267
- if (contentTags.includes(tag)) {
1268
- return false;
1269
- }
1270
- this.doc.transact(() => {
1271
- const newContentTags = [...contentTags, tag];
1272
- entryMap.set("attributes", {
1273
- ...attributes,
1274
- contentTags: newContentTags,
1275
- tags: newContentTags
1276
- // Sync legacy tags array
1277
- });
1278
- });
1279
- this.notifyGlobalListeners();
1280
- return true;
2225
+ return TagManager.addTag(this, key, tag);
1281
2226
  }
1282
2227
  /**
1283
2228
  * Remove a content tag from a key.
1284
2229
  * @returns true if the tag was removed
1285
2230
  */
1286
2231
  removeTag(key, tag) {
1287
- if (key === "$date" || key === "$time" || key === "$version") {
1288
- return false;
1289
- }
1290
- const entryMap = this.rootMap.get(key);
1291
- if (!entryMap) {
1292
- return false;
1293
- }
1294
- const attributes = entryMap.get("attributes");
1295
- const contentTags = attributes?.contentTags || [];
1296
- const tagIndex = contentTags.indexOf(tag);
1297
- if (tagIndex === -1) {
1298
- return false;
1299
- }
1300
- this.doc.transact(() => {
1301
- const newContentTags = contentTags.filter((t) => t !== tag);
1302
- entryMap.set("attributes", {
1303
- ...attributes,
1304
- contentTags: newContentTags,
1305
- tags: newContentTags
1306
- // Sync legacy tags array
1307
- });
1308
- });
1309
- this.notifyGlobalListeners();
1310
- return true;
2232
+ return TagManager.removeTag(this, key, tag);
1311
2233
  }
1312
2234
  /**
1313
2235
  * Get all content tags for a key.
1314
2236
  */
1315
2237
  getTags(key) {
1316
- if (key === "$date" || key === "$time" || key === "$version") {
1317
- return [];
1318
- }
1319
- const entryMap = this.rootMap.get(key);
1320
- if (!entryMap) {
1321
- return [];
1322
- }
1323
- const attributes = entryMap.get("attributes");
1324
- return attributes?.contentTags || [];
2238
+ return TagManager.getTags(this, key);
1325
2239
  }
1326
2240
  /**
1327
2241
  * Get all unique content tags across all keys.
1328
2242
  */
1329
2243
  getAllTags() {
1330
- const allTags = /* @__PURE__ */ new Set();
1331
- for (const [, val] of this.rootMap) {
1332
- const entryMap = val;
1333
- const attributes = entryMap.get("attributes");
1334
- if (attributes?.contentTags) {
1335
- attributes.contentTags.forEach((tag) => allTags.add(tag));
1336
- }
1337
- }
1338
- return Array.from(allTags);
2244
+ return TagManager.getAllTags(this);
1339
2245
  }
1340
2246
  /**
1341
2247
  * Check if a key has a specific content tag.
1342
2248
  */
1343
2249
  hasTag(key, tag) {
1344
- if (key === "$date" || key === "$time" || key === "$version") {
1345
- return false;
1346
- }
1347
- const entryMap = this.rootMap.get(key);
1348
- if (!entryMap) {
1349
- return false;
1350
- }
1351
- const attributes = entryMap.get("attributes");
1352
- return attributes?.contentTags?.includes(tag) || false;
2250
+ return TagManager.hasTag(this, key, tag);
1353
2251
  }
1354
2252
  /**
1355
2253
  * Get all keys with a specific content tag as formatted string.
1356
2254
  */
1357
- getTagged(tag) {
1358
- const entries = [];
1359
- const keys = this.getSortedKeys();
1360
- keys.forEach((key) => {
1361
- if (this.hasTag(key, tag)) {
1362
- entries.push([key, this.get_value(key)]);
1363
- }
1364
- });
1365
- return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
2255
+ getTagged(tag) {
2256
+ return TagManager.getTagged(this, tag);
1366
2257
  }
1367
2258
  /**
1368
2259
  * Get array of keys with a specific content tag.
1369
2260
  */
1370
2261
  getKeysByTag(tag) {
1371
- const keys = this.getSortedKeys();
1372
- return keys.filter((key) => this.hasTag(key, tag));
2262
+ return TagManager.getKeysByTag(this, tag);
1373
2263
  }
1374
2264
  // ============================================
1375
2265
  // System Tag Methods (requires system access level)
1376
2266
  // ============================================
1377
2267
  /**
1378
2268
  * Add a system tag to a key (requires system access).
1379
- * System tags: 'SystemPrompt', 'LLMRead', 'LLMWrite', 'readonly', 'protected', 'ApplyTemplate'
2269
+ * System tags: 'SystemPrompt', 'LLMRead', 'LLMWrite', 'ApplyTemplate'
1380
2270
  */
1381
2271
  systemAddTag(key, tag) {
1382
- if (!this.hasSystemAccess) {
1383
- console.warn("MindCache: systemAddTag requires system access level");
1384
- return false;
1385
- }
1386
- if (key === "$date" || key === "$time" || key === "$version") {
1387
- return false;
1388
- }
1389
- const entryMap = this.rootMap.get(key);
1390
- if (!entryMap) {
1391
- return false;
1392
- }
1393
- const attributes = entryMap.get("attributes");
1394
- const systemTags = attributes?.systemTags || [];
1395
- if (systemTags.includes(tag)) {
1396
- return false;
1397
- }
1398
- this.doc.transact(() => {
1399
- const newSystemTags = [...systemTags, tag];
1400
- const normalizedTags = this.normalizeSystemTags(newSystemTags);
1401
- entryMap.set("attributes", {
1402
- ...attributes,
1403
- systemTags: normalizedTags
1404
- });
1405
- });
1406
- this.notifyGlobalListeners();
1407
- return true;
2272
+ return TagManager.systemAddTag(this, key, tag);
1408
2273
  }
1409
2274
  /**
1410
2275
  * Remove a system tag from a key (requires system access).
1411
2276
  */
1412
2277
  systemRemoveTag(key, tag) {
1413
- if (!this.hasSystemAccess) {
1414
- console.warn("MindCache: systemRemoveTag requires system access level");
1415
- return false;
1416
- }
1417
- if (key === "$date" || key === "$time" || key === "$version") {
1418
- return false;
1419
- }
1420
- const entryMap = this.rootMap.get(key);
1421
- if (!entryMap) {
1422
- return false;
1423
- }
1424
- const attributes = entryMap.get("attributes");
1425
- const systemTags = attributes?.systemTags || [];
1426
- const tagIndex = systemTags.indexOf(tag);
1427
- if (tagIndex === -1) {
1428
- return false;
1429
- }
1430
- this.doc.transact(() => {
1431
- const newSystemTags = systemTags.filter((t) => t !== tag);
1432
- entryMap.set("attributes", {
1433
- ...attributes,
1434
- systemTags: newSystemTags
1435
- });
1436
- });
1437
- this.notifyGlobalListeners();
1438
- return true;
2278
+ return TagManager.systemRemoveTag(this, key, tag);
1439
2279
  }
1440
2280
  /**
1441
2281
  * Get all system tags for a key (requires system access).
1442
2282
  */
1443
2283
  systemGetTags(key) {
1444
- if (!this.hasSystemAccess) {
1445
- console.warn("MindCache: systemGetTags requires system access level");
1446
- return [];
1447
- }
1448
- if (key === "$date" || key === "$time" || key === "$version") {
1449
- return [];
1450
- }
1451
- const entryMap = this.rootMap.get(key);
1452
- if (!entryMap) {
1453
- return [];
1454
- }
1455
- const attributes = entryMap.get("attributes");
1456
- return attributes?.systemTags || [];
2284
+ return TagManager.systemGetTags(this, key);
1457
2285
  }
1458
2286
  /**
1459
2287
  * Check if a key has a specific system tag (requires system access).
1460
2288
  */
1461
2289
  systemHasTag(key, tag) {
1462
- if (!this.hasSystemAccess) {
1463
- console.warn("MindCache: systemHasTag requires system access level");
1464
- return false;
1465
- }
1466
- if (key === "$date" || key === "$time" || key === "$version") {
1467
- return false;
1468
- }
1469
- const entryMap = this.rootMap.get(key);
1470
- if (!entryMap) {
1471
- return false;
1472
- }
1473
- const attributes = entryMap.get("attributes");
1474
- return attributes?.systemTags?.includes(tag) || false;
2290
+ return TagManager.systemHasTag(this, key, tag);
1475
2291
  }
1476
2292
  /**
1477
2293
  * Set all system tags for a key at once (requires system access).
1478
2294
  */
1479
2295
  systemSetTags(key, tags) {
1480
- if (!this.hasSystemAccess) {
1481
- console.warn("MindCache: systemSetTags requires system access level");
1482
- return false;
1483
- }
1484
- if (key === "$date" || key === "$time" || key === "$version") {
1485
- return false;
1486
- }
1487
- const entryMap = this.rootMap.get(key);
1488
- if (!entryMap) {
1489
- return false;
1490
- }
1491
- this.doc.transact(() => {
1492
- const attributes = entryMap.get("attributes");
1493
- entryMap.set("attributes", {
1494
- ...attributes,
1495
- systemTags: [...tags]
1496
- });
1497
- });
1498
- this.notifyGlobalListeners();
1499
- return true;
2296
+ return TagManager.systemSetTags(this, key, tags);
1500
2297
  }
1501
2298
  /**
1502
2299
  * Get all keys with a specific system tag (requires system access).
1503
2300
  */
1504
2301
  systemGetKeysByTag(tag) {
1505
- if (!this.hasSystemAccess) {
1506
- console.warn("MindCache: systemGetKeysByTag requires system access level");
1507
- return [];
1508
- }
1509
- const keys = this.getSortedKeys();
1510
- return keys.filter((key) => this.systemHasTag(key, tag));
2302
+ return TagManager.systemGetKeysByTag(this, tag);
1511
2303
  }
1512
2304
  /**
1513
2305
  * Helper to get sorted keys (by zIndex).
2306
+ * Respects context filtering when set.
1514
2307
  */
1515
2308
  getSortedKeys() {
1516
2309
  const entries = [];
1517
2310
  for (const [key, val] of this.rootMap) {
1518
- const entryMap = val;
1519
- const attributes = entryMap.get("attributes");
1520
- entries.push({ key, zIndex: attributes?.zIndex ?? 0 });
1521
- }
1522
- return entries.sort((a, b) => a.zIndex - b.zIndex).map((e) => e.key);
1523
- }
1524
- /**
1525
- * Serialize to JSON string.
1526
- */
1527
- toJSON() {
1528
- return JSON.stringify(this.serialize());
1529
- }
1530
- /**
1531
- * Deserialize from JSON string.
1532
- */
1533
- fromJSON(jsonString) {
1534
- try {
1535
- const data = JSON.parse(jsonString);
1536
- this.deserialize(data);
1537
- } catch (error) {
1538
- console.error("MindCache: Failed to deserialize JSON:", error);
1539
- }
1540
- }
1541
- /**
1542
- * Export to Markdown format.
1543
- */
1544
- toMarkdown() {
1545
- const now = /* @__PURE__ */ new Date();
1546
- const lines = [];
1547
- const appendixEntries = [];
1548
- let appendixCounter = 0;
1549
- lines.push("# MindCache STM Export");
1550
- lines.push("");
1551
- lines.push(`Export Date: ${now.toISOString().split("T")[0]}`);
1552
- lines.push("");
1553
- lines.push("---");
1554
- lines.push("");
1555
- lines.push("## STM Entries");
1556
- lines.push("");
1557
- const sortedKeys = this.getSortedKeys();
1558
- sortedKeys.forEach((key) => {
1559
- const entryMap = this.rootMap.get(key);
1560
- if (!entryMap) {
1561
- return;
1562
- }
1563
- const attributes = entryMap.get("attributes");
1564
- const value = entryMap.get("value");
1565
- if (attributes?.systemTags?.includes("protected")) {
1566
- return;
1567
- }
1568
- lines.push(`### ${key}`);
1569
- const entryType = attributes?.type || "text";
1570
- lines.push(`- **Type**: \`${entryType}\``);
1571
- lines.push(`- **System Tags**: \`${attributes?.systemTags?.join(", ") || "none"}\``);
1572
- lines.push(`- **Z-Index**: \`${attributes?.zIndex ?? 0}\``);
1573
- if (attributes?.contentTags && attributes.contentTags.length > 0) {
1574
- lines.push(`- **Tags**: \`${attributes.contentTags.join("`, `")}\``);
1575
- }
1576
- if (attributes?.contentType) {
1577
- lines.push(`- **Content Type**: \`${attributes.contentType}\``);
1578
- }
1579
- if (entryType === "image" || entryType === "file") {
1580
- const label = String.fromCharCode(65 + appendixCounter);
1581
- appendixCounter++;
1582
- lines.push(`- **Value**: [See Appendix ${label}]`);
1583
- appendixEntries.push({
1584
- key,
1585
- type: entryType,
1586
- contentType: attributes?.contentType || "application/octet-stream",
1587
- base64: value,
1588
- label
1589
- });
1590
- } else if (entryType === "json") {
1591
- lines.push("- **Value**:");
1592
- lines.push("```json");
1593
- try {
1594
- const jsonValue = typeof value === "string" ? value : JSON.stringify(value, null, 2);
1595
- lines.push(jsonValue);
1596
- } catch {
1597
- lines.push(String(value));
1598
- }
1599
- lines.push("```");
1600
- } else {
1601
- lines.push(`- **Value**: ${value}`);
1602
- }
1603
- lines.push("");
1604
- });
1605
- if (appendixEntries.length > 0) {
1606
- lines.push("---");
1607
- lines.push("");
1608
- lines.push("## Appendix: Binary Data");
1609
- lines.push("");
1610
- appendixEntries.forEach((entry) => {
1611
- lines.push(`### Appendix ${entry.label}: ${entry.key}`);
1612
- lines.push(`- **Type**: \`${entry.type}\``);
1613
- lines.push(`- **Content Type**: \`${entry.contentType}\``);
1614
- lines.push("- **Base64 Data**:");
1615
- lines.push("```");
1616
- lines.push(entry.base64);
1617
- lines.push("```");
1618
- lines.push("");
1619
- });
1620
- }
1621
- return lines.join("\n");
1622
- }
1623
- /**
1624
- * Import from Markdown format.
1625
- */
1626
- fromMarkdown(markdown) {
1627
- const lines = markdown.split("\n");
1628
- let currentKey = null;
1629
- let currentAttributes = {};
1630
- let currentValue = null;
1631
- let inCodeBlock = false;
1632
- let codeBlockContent = [];
1633
- for (const line of lines) {
1634
- if (line.startsWith("### ") && !line.startsWith("### Appendix")) {
1635
- if (currentKey && currentValue !== null) {
1636
- this.set_value(currentKey, currentValue, currentAttributes);
1637
- }
1638
- currentKey = line.substring(4).trim();
1639
- currentAttributes = {};
1640
- currentValue = null;
1641
- continue;
1642
- }
1643
- if (line.startsWith("### Appendix ")) {
1644
- const match = line.match(/### Appendix ([A-Z]): (.+)/);
1645
- if (match) {
1646
- currentKey = match[2];
1647
- }
1648
- continue;
1649
- }
1650
- if (line.startsWith("- **Type**:")) {
1651
- const type = line.match(/`(.+)`/)?.[1];
1652
- if (type) {
1653
- currentAttributes.type = type;
1654
- }
1655
- continue;
1656
- }
1657
- if (line.startsWith("- **System Tags**:")) {
1658
- const tagsStr = line.match(/`([^`]+)`/)?.[1] || "";
1659
- if (tagsStr !== "none") {
1660
- currentAttributes.systemTags = tagsStr.split(", ").filter((t) => t);
1661
- }
1662
- continue;
1663
- }
1664
- if (line.startsWith("- **Z-Index**:")) {
1665
- const zIndex = parseInt(line.match(/`(\d+)`/)?.[1] || "0", 10);
1666
- currentAttributes.zIndex = zIndex;
1667
- continue;
1668
- }
1669
- if (line.startsWith("- **Tags**:")) {
1670
- const tags = line.match(/`([^`]+)`/g)?.map((t) => t.slice(1, -1)) || [];
1671
- currentAttributes.contentTags = tags;
1672
- continue;
1673
- }
1674
- if (line.startsWith("- **Content Type**:")) {
1675
- currentAttributes.contentType = line.match(/`(.+)`/)?.[1];
1676
- continue;
1677
- }
1678
- if (line.startsWith("- **Value**:") && !line.includes("[See Appendix")) {
1679
- currentValue = line.substring(12).trim();
1680
- continue;
1681
- }
1682
- if (line === "```json" || line === "```") {
1683
- if (inCodeBlock) {
1684
- inCodeBlock = false;
1685
- if (currentKey && codeBlockContent.length > 0) {
1686
- currentValue = codeBlockContent.join("\n");
1687
- }
1688
- codeBlockContent = [];
1689
- } else {
1690
- inCodeBlock = true;
1691
- }
1692
- continue;
1693
- }
1694
- if (inCodeBlock) {
1695
- codeBlockContent.push(line);
2311
+ if (!this.keyMatchesContext(key)) {
2312
+ continue;
1696
2313
  }
2314
+ const entryMap = val;
2315
+ const attributes = entryMap.get("attributes");
2316
+ entries.push({ key, zIndex: attributes?.zIndex ?? 0 });
1697
2317
  }
1698
- if (currentKey && currentValue !== null) {
1699
- this.set_value(currentKey, currentValue, currentAttributes);
2318
+ return entries.sort((a, b) => a.zIndex - b.zIndex).map((e) => e.key);
2319
+ }
2320
+ /**
2321
+ * Serialize to JSON string.
2322
+ */
2323
+ toJSON() {
2324
+ return JSON.stringify(this.serialize());
2325
+ }
2326
+ /**
2327
+ * Deserialize from JSON string.
2328
+ */
2329
+ fromJSON(jsonString) {
2330
+ try {
2331
+ const data = JSON.parse(jsonString);
2332
+ this.deserialize(data);
2333
+ } catch (error) {
2334
+ console.error("MindCache: Failed to deserialize JSON:", error);
1700
2335
  }
1701
2336
  }
2337
+ /**
2338
+ * Export to Markdown format.
2339
+ */
2340
+ toMarkdown() {
2341
+ return MarkdownSerializer.toMarkdown(this);
2342
+ }
2343
+ /**
2344
+ * Import from Markdown format.
2345
+ * @param markdown The markdown string to import
2346
+ * @param merge If false (default), clears existing data before importing. If true, merges with existing data.
2347
+ */
2348
+ fromMarkdown(markdown, merge = false) {
2349
+ MarkdownSerializer.fromMarkdown(markdown, this, merge);
2350
+ }
1702
2351
  /**
1703
2352
  * Set base64 binary data.
1704
2353
  */
@@ -1779,9 +2428,6 @@ var MindCache = class {
1779
2428
  * Note: This exposes Yjs Y.Text directly for editor bindings (y-quill, y-codemirror, etc.)
1780
2429
  */
1781
2430
  set_document(key, initialText, attributes) {
1782
- if (key === "$date" || key === "$time" || key === "$version") {
1783
- return;
1784
- }
1785
2431
  let entryMap = this.rootMap.get(key);
1786
2432
  if (!entryMap) {
1787
2433
  entryMap = new Y.Map();
@@ -1840,7 +2486,7 @@ var MindCache = class {
1840
2486
  }
1841
2487
  }
1842
2488
  /**
1843
- * Replace all text in a document key (private - use set_value for public API).
2489
+ * Replace all text in a document key.
1844
2490
  * Uses diff-based updates when changes are < diffThreshold (default 80%).
1845
2491
  * This preserves concurrent edits and provides better undo granularity.
1846
2492
  */
@@ -1910,16 +2556,11 @@ var MindCache = class {
1910
2556
  }
1911
2557
  // Sanitize key name for use in tool names
1912
2558
  sanitizeKeyForTool(key) {
1913
- return key.replace(/[^a-zA-Z0-9_-]/g, "_");
2559
+ return AIToolBuilder.sanitizeKeyForTool(key);
1914
2560
  }
1915
2561
  // Find original key from sanitized tool name
1916
2562
  findKeyFromSanitizedTool(sanitizedKey) {
1917
- for (const [key] of this.rootMap) {
1918
- if (this.sanitizeKeyForTool(key) === sanitizedKey) {
1919
- return key;
1920
- }
1921
- }
1922
- return void 0;
2563
+ return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
1923
2564
  }
1924
2565
  /**
1925
2566
  * Generate Vercel AI SDK compatible tools for writable keys.
@@ -1930,130 +2571,7 @@ var MindCache = class {
1930
2571
  * - Prevents LLMs from escalating privileges
1931
2572
  */
1932
2573
  create_vercel_ai_tools() {
1933
- const tools = {};
1934
- for (const [key, val] of this.rootMap) {
1935
- if (key.startsWith("$")) {
1936
- continue;
1937
- }
1938
- const entryMap = val;
1939
- const attributes = entryMap.get("attributes");
1940
- const isWritable = attributes?.systemTags?.includes("LLMWrite");
1941
- if (!isWritable) {
1942
- continue;
1943
- }
1944
- const sanitizedKey = this.sanitizeKeyForTool(key);
1945
- const isDocument = attributes?.type === "document";
1946
- tools[`write_${sanitizedKey}`] = {
1947
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
1948
- inputSchema: {
1949
- type: "object",
1950
- properties: {
1951
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1952
- },
1953
- required: ["value"]
1954
- },
1955
- execute: async ({ value }) => {
1956
- const success = this.llm_set_key(key, value);
1957
- if (success) {
1958
- return {
1959
- result: `Successfully wrote "${value}" to ${key}`,
1960
- key,
1961
- value
1962
- };
1963
- }
1964
- return {
1965
- result: `Failed to write to ${key} - permission denied or key not found`,
1966
- key,
1967
- error: true
1968
- };
1969
- }
1970
- };
1971
- if (isDocument) {
1972
- tools[`append_${sanitizedKey}`] = {
1973
- description: `Append text to the end of "${key}" document`,
1974
- inputSchema: {
1975
- type: "object",
1976
- properties: {
1977
- text: { type: "string", description: "Text to append" }
1978
- },
1979
- required: ["text"]
1980
- },
1981
- execute: async ({ text }) => {
1982
- if (!attributes?.systemTags?.includes("LLMWrite")) {
1983
- return { result: `Permission denied for ${key}`, key, error: true };
1984
- }
1985
- const yText = this.get_document(key);
1986
- if (yText) {
1987
- yText.insert(yText.length, text);
1988
- return {
1989
- result: `Successfully appended to ${key}`,
1990
- key,
1991
- appended: text
1992
- };
1993
- }
1994
- return { result: `Document ${key} not found`, key };
1995
- }
1996
- };
1997
- tools[`insert_${sanitizedKey}`] = {
1998
- description: `Insert text at a position in "${key}" document`,
1999
- inputSchema: {
2000
- type: "object",
2001
- properties: {
2002
- index: { type: "number", description: "Position to insert at (0 = start)" },
2003
- text: { type: "string", description: "Text to insert" }
2004
- },
2005
- required: ["index", "text"]
2006
- },
2007
- execute: async ({ index, text }) => {
2008
- if (!attributes?.systemTags?.includes("LLMWrite")) {
2009
- return { result: `Permission denied for ${key}`, key, error: true };
2010
- }
2011
- this.insert_text(key, index, text);
2012
- return {
2013
- result: `Successfully inserted text at position ${index} in ${key}`,
2014
- key,
2015
- index,
2016
- inserted: text
2017
- };
2018
- }
2019
- };
2020
- tools[`edit_${sanitizedKey}`] = {
2021
- description: `Find and replace text in "${key}" document`,
2022
- inputSchema: {
2023
- type: "object",
2024
- properties: {
2025
- find: { type: "string", description: "Text to find" },
2026
- replace: { type: "string", description: "Replacement text" }
2027
- },
2028
- required: ["find", "replace"]
2029
- },
2030
- execute: async ({ find, replace }) => {
2031
- if (!attributes?.systemTags?.includes("LLMWrite")) {
2032
- return { result: `Permission denied for ${key}`, key, error: true };
2033
- }
2034
- const yText = this.get_document(key);
2035
- if (yText) {
2036
- const text = yText.toString();
2037
- const idx = text.indexOf(find);
2038
- if (idx !== -1) {
2039
- yText.delete(idx, find.length);
2040
- yText.insert(idx, replace);
2041
- return {
2042
- result: `Successfully replaced "${find}" with "${replace}" in ${key}`,
2043
- key,
2044
- find,
2045
- replace,
2046
- index: idx
2047
- };
2048
- }
2049
- return { result: `Text "${find}" not found in ${key}`, key };
2050
- }
2051
- return { result: `Document ${key} not found`, key };
2052
- }
2053
- };
2054
- }
2055
- }
2056
- return tools;
2574
+ return AIToolBuilder.createVercelAITools(this);
2057
2575
  }
2058
2576
  /**
2059
2577
  * @deprecated Use create_vercel_ai_tools() instead
@@ -2066,121 +2584,14 @@ var MindCache = class {
2066
2584
  * Indicates which tools can be used to modify writable keys.
2067
2585
  */
2068
2586
  get_system_prompt() {
2069
- const lines = [];
2070
- for (const [key, val] of this.rootMap) {
2071
- if (key.startsWith("$")) {
2072
- continue;
2073
- }
2074
- const entryMap = val;
2075
- const attributes = entryMap.get("attributes");
2076
- const isVisible = attributes?.systemTags?.includes("SystemPrompt") || attributes?.systemTags?.includes("LLMRead");
2077
- if (!isVisible) {
2078
- continue;
2079
- }
2080
- const value = this.get_value(key);
2081
- const displayValue = typeof value === "object" ? JSON.stringify(value) : value;
2082
- const isWritable = attributes?.systemTags?.includes("LLMWrite");
2083
- const isDocument = attributes?.type === "document";
2084
- const sanitizedKey = this.sanitizeKeyForTool(key);
2085
- if (isWritable) {
2086
- if (isDocument) {
2087
- lines.push(
2088
- `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
2089
- );
2090
- } else {
2091
- const oldValueHint = displayValue ? ` This tool DOES NOT append \u2014 start your response with the old value (${displayValue})` : "";
2092
- lines.push(
2093
- `${key}: ${displayValue}. You can rewrite "${key}" by using the write_${sanitizedKey} tool.${oldValueHint}`
2094
- );
2095
- }
2096
- } else {
2097
- lines.push(`${key}: ${displayValue}`);
2098
- }
2099
- }
2100
- lines.push(`$date: ${this.get_value("$date")}`);
2101
- lines.push(`$time: ${this.get_value("$time")}`);
2102
- return lines.join(", ");
2587
+ return AIToolBuilder.getSystemPrompt(this);
2103
2588
  }
2104
2589
  /**
2105
2590
  * Execute a tool call by name with the given value.
2106
2591
  * Returns the result or null if tool not found.
2107
2592
  */
2108
2593
  executeToolCall(toolName, value) {
2109
- const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
2110
- if (!match) {
2111
- return null;
2112
- }
2113
- const [, action, sanitizedKey] = match;
2114
- const key = this.findKeyFromSanitizedTool(sanitizedKey);
2115
- if (!key) {
2116
- return null;
2117
- }
2118
- const entryMap = this.rootMap.get(key);
2119
- if (!entryMap) {
2120
- return null;
2121
- }
2122
- const attributes = entryMap.get("attributes");
2123
- const isWritable = attributes?.systemTags?.includes("LLMWrite");
2124
- if (!isWritable) {
2125
- return null;
2126
- }
2127
- const isDocument = attributes?.type === "document";
2128
- switch (action) {
2129
- case "write":
2130
- if (isDocument) {
2131
- this._replaceDocumentText(key, value);
2132
- } else {
2133
- this.set_value(key, value);
2134
- }
2135
- return {
2136
- result: `Successfully wrote "${value}" to ${key}`,
2137
- key,
2138
- value
2139
- };
2140
- case "append":
2141
- if (isDocument) {
2142
- const yText = this.get_document(key);
2143
- if (yText) {
2144
- yText.insert(yText.length, value);
2145
- return {
2146
- result: `Successfully appended to ${key}`,
2147
- key,
2148
- value
2149
- };
2150
- }
2151
- }
2152
- return null;
2153
- case "insert":
2154
- if (isDocument && typeof value === "object" && value.index !== void 0 && value.text) {
2155
- this.insert_text(key, value.index, value.text);
2156
- return {
2157
- result: `Successfully inserted at position ${value.index} in ${key}`,
2158
- key,
2159
- value: value.text
2160
- };
2161
- }
2162
- return null;
2163
- case "edit":
2164
- if (isDocument && typeof value === "object" && value.find && value.replace !== void 0) {
2165
- const yText = this.get_document(key);
2166
- if (yText) {
2167
- const text = yText.toString();
2168
- const idx = text.indexOf(value.find);
2169
- if (idx !== -1) {
2170
- yText.delete(idx, value.find.length);
2171
- yText.insert(idx, value.replace);
2172
- return {
2173
- result: `Successfully replaced "${value.find}" with "${value.replace}" in ${key}`,
2174
- key,
2175
- value: value.replace
2176
- };
2177
- }
2178
- }
2179
- }
2180
- return null;
2181
- default:
2182
- return null;
2183
- }
2594
+ return AIToolBuilder.executeToolCall(this, toolName, value);
2184
2595
  }
2185
2596
  // Internal method stub for legacy compatibility
2186
2597
  _setFromRemote(_key, _value, _attributes) {