mindcache 3.4.3 → 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.
@@ -436,6 +436,868 @@ var DEFAULT_KEY_ATTRIBUTES = {
436
436
  zIndex: 0
437
437
  };
438
438
 
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));
491
+ }
492
+ lines.push("```");
493
+ } else {
494
+ lines.push("- **Value**:");
495
+ lines.push("```");
496
+ lines.push(String(value));
497
+ lines.push("```");
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
+ });
516
+ }
517
+ return lines.join("\n");
518
+ }
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);
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 = "";
608
+ } else {
609
+ currentValue = afterValue;
610
+ }
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");
619
+ }
620
+ codeBlockContent = [];
621
+ } else {
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;
637
+ }
638
+ }
639
+ }
640
+ if (currentKey && currentValue !== null) {
641
+ mc.set_value(currentKey, currentValue.trim(), currentAttributes);
642
+ }
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
651
+ });
652
+ }
653
+ }
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
+
439
1301
  // src/core/MindCache.ts
440
1302
  var MindCache = class {
441
1303
  // Public doc for adapter access
@@ -453,7 +1315,7 @@ var MindCache = class {
453
1315
  const normalized = [];
454
1316
  const seen = /* @__PURE__ */ new Set();
455
1317
  for (const tag of tags) {
456
- if (["SystemPrompt", "LLMRead", "LLMWrite", "protected", "ApplyTemplate"].includes(tag)) {
1318
+ if (["SystemPrompt", "LLMRead", "LLMWrite", "ApplyTemplate"].includes(tag)) {
457
1319
  if (!seen.has(tag)) {
458
1320
  seen.add(tag);
459
1321
  normalized.push(tag);
@@ -468,8 +1330,10 @@ var MindCache = class {
468
1330
  _isLoaded = true;
469
1331
  // Default true for local mode
470
1332
  _cloudConfig = null;
471
- // Access level for system operations
1333
+ // Access level for admin operations
472
1334
  _accessLevel = "user";
1335
+ // Context filtering (client-local, not persisted)
1336
+ _contextRules = null;
473
1337
  _initPromise = null;
474
1338
  // Y-IndexedDB provider
475
1339
  _idbProvider = null;
@@ -534,6 +1398,9 @@ var MindCache = class {
534
1398
  if (options?.accessLevel) {
535
1399
  this._accessLevel = options.accessLevel;
536
1400
  }
1401
+ if (options?.context) {
1402
+ this._contextRules = options.context;
1403
+ }
537
1404
  const initPromises = [];
538
1405
  if (options?.cloud) {
539
1406
  this._cloudConfig = options.cloud;
@@ -708,7 +1575,101 @@ var MindCache = class {
708
1575
  return this._accessLevel;
709
1576
  }
710
1577
  get hasSystemAccess() {
711
- 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
+ });
712
1673
  }
713
1674
  async _initCloud() {
714
1675
  if (!this._cloudConfig) {
@@ -883,10 +1844,11 @@ var MindCache = class {
883
1844
  this.rootMap.set(key, entryMap);
884
1845
  entryMap.set("value", entry.value);
885
1846
  const attrs = entry.attributes || {};
1847
+ const contentTags = attrs.contentTags || attrs.tags || [];
886
1848
  const normalizedAttrs = {
887
1849
  type: attrs.type || "text",
888
1850
  contentType: attrs.contentType,
889
- contentTags: attrs.contentTags || [],
1851
+ contentTags,
890
1852
  systemTags: this.normalizeSystemTags(attrs.systemTags || []),
891
1853
  zIndex: attrs.zIndex ?? 0
892
1854
  };
@@ -929,10 +1891,21 @@ var MindCache = class {
929
1891
  return false;
930
1892
  }
931
1893
  // InjectSTM replacement (private helper)
1894
+ // Handles special template variables: $date, $time, $version
932
1895
  _injectSTMInternal(template, _processingStack) {
933
1896
  return template.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
934
- const val = this.get_value(key.trim(), _processingStack);
935
- 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) : "";
936
1909
  });
937
1910
  }
938
1911
  /**
@@ -947,10 +1920,10 @@ var MindCache = class {
947
1920
  getAll() {
948
1921
  const result = {};
949
1922
  for (const [key] of this.rootMap) {
950
- result[key] = this.get_value(key);
1923
+ if (this.keyMatchesContext(key)) {
1924
+ result[key] = this.get_value(key);
1925
+ }
951
1926
  }
952
- result["$date"] = this.get_value("$date");
953
- result["$time"] = this.get_value("$time");
954
1927
  return result;
955
1928
  }
956
1929
  /**
@@ -961,30 +1934,24 @@ var MindCache = class {
961
1934
  getAllEntries() {
962
1935
  const result = {};
963
1936
  for (const [key] of this.rootMap) {
964
- const value = this.get_value(key);
965
- const attributes = this.get_attributes(key);
966
- if (attributes) {
967
- 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
+ }
968
1943
  }
969
1944
  }
970
1945
  return result;
971
1946
  }
972
1947
  get_value(key, _processingStack) {
973
- if (key === "$date") {
974
- const today = /* @__PURE__ */ new Date();
975
- return today.toISOString().split("T")[0];
976
- }
977
- if (key === "$time") {
978
- const now = /* @__PURE__ */ new Date();
979
- return now.toTimeString().split(" ")[0];
980
- }
981
- if (key === "$version") {
982
- return this.version;
983
- }
984
1948
  const entryMap = this.rootMap.get(key);
985
1949
  if (!entryMap) {
986
1950
  return void 0;
987
1951
  }
1952
+ if (!this.keyMatchesContext(key)) {
1953
+ return void 0;
1954
+ }
988
1955
  const attributes = entryMap.get("attributes");
989
1956
  const value = entryMap.get("value");
990
1957
  if (attributes?.type === "document" && value instanceof Y.Text) {
@@ -1003,28 +1970,21 @@ var MindCache = class {
1003
1970
  return value;
1004
1971
  }
1005
1972
  get_attributes(key) {
1006
- if (key === "$date" || key === "$time" || key === "$version") {
1007
- return {
1008
- type: "text",
1009
- contentTags: [],
1010
- systemTags: ["SystemPrompt", "protected"],
1011
- zIndex: 999999
1012
- };
1013
- }
1014
1973
  const entryMap = this.rootMap.get(key);
1015
1974
  return entryMap ? entryMap.get("attributes") : void 0;
1016
1975
  }
1017
1976
  /**
1018
1977
  * Update only the attributes of a key without modifying the value.
1019
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
1020
1980
  */
1021
1981
  set_attributes(key, attributes) {
1022
- if (key === "$date" || key === "$time" || key === "$version") {
1023
- return;
1024
- }
1025
1982
  const entryMap = this.rootMap.get(key);
1026
1983
  if (!entryMap) {
1027
- 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`);
1028
1988
  }
1029
1989
  this.doc.transact(() => {
1030
1990
  const existingAttrs = entryMap.get("attributes");
@@ -1042,13 +2002,14 @@ var MindCache = class {
1042
2002
  entryMap.set("value", currentValue.toString());
1043
2003
  }
1044
2004
  });
2005
+ return true;
1045
2006
  }
1046
2007
  set_value(key, value, attributes) {
1047
- if (key === "$date" || key === "$time" || key === "$version") {
1048
- return;
1049
- }
1050
2008
  const existingEntry = this.rootMap.get(key);
1051
2009
  if (existingEntry) {
2010
+ if (this._contextRules && !this.keyMatchesContext(key)) {
2011
+ throw new Error(`Cannot modify key "${key}": does not match current context`);
2012
+ }
1052
2013
  const existingAttrs = existingEntry.get("attributes");
1053
2014
  if (existingAttrs?.type === "document") {
1054
2015
  if (!attributes?.type || attributes.type === "document") {
@@ -1062,6 +2023,10 @@ var MindCache = class {
1062
2023
  }
1063
2024
  }
1064
2025
  }
2026
+ if (!existingEntry && this._contextRules) {
2027
+ this.create_key(key, value, attributes);
2028
+ return;
2029
+ }
1065
2030
  if (!existingEntry && attributes?.type === "document") {
1066
2031
  this.set_document(key, typeof value === "string" ? value : "", attributes);
1067
2032
  return;
@@ -1102,9 +2067,6 @@ var MindCache = class {
1102
2067
  * Used by create_vercel_ai_tools() to prevent LLMs from escalating privileges.
1103
2068
  */
1104
2069
  llm_set_key(key, value) {
1105
- if (key === "$date" || key === "$time" || key === "$version") {
1106
- return false;
1107
- }
1108
2070
  const entryMap = this.rootMap.get(key);
1109
2071
  if (!entryMap) {
1110
2072
  return false;
@@ -1125,9 +2087,6 @@ var MindCache = class {
1125
2087
  return true;
1126
2088
  }
1127
2089
  delete_key(key) {
1128
- if (key === "$date" || key === "$time") {
1129
- return;
1130
- }
1131
2090
  this.rootMap.delete(key);
1132
2091
  }
1133
2092
  clear() {
@@ -1143,19 +2102,16 @@ var MindCache = class {
1143
2102
  * Check if a key exists in MindCache.
1144
2103
  */
1145
2104
  has(key) {
1146
- if (key === "$date" || key === "$time" || key === "$version") {
1147
- return true;
2105
+ if (!this.rootMap.has(key)) {
2106
+ return false;
1148
2107
  }
1149
- return this.rootMap.has(key);
2108
+ return this.keyMatchesContext(key);
1150
2109
  }
1151
2110
  /**
1152
2111
  * Delete a key from MindCache.
1153
2112
  * @returns true if the key existed and was deleted
1154
2113
  */
1155
2114
  delete(key) {
1156
- if (key === "$date" || key === "$time" || key === "$version") {
1157
- return false;
1158
- }
1159
2115
  if (!this.rootMap.has(key)) {
1160
2116
  return false;
1161
2117
  }
@@ -1181,9 +2137,7 @@ var MindCache = class {
1181
2137
  update(data) {
1182
2138
  this.doc.transact(() => {
1183
2139
  for (const [key, value] of Object.entries(data)) {
1184
- if (key !== "$date" && key !== "$time" && key !== "$version") {
1185
- this.set_value(key, value);
1186
- }
2140
+ this.set_value(key, value);
1187
2141
  }
1188
2142
  });
1189
2143
  this.notifyGlobalListeners();
@@ -1192,38 +2146,48 @@ var MindCache = class {
1192
2146
  * Get the number of keys in MindCache.
1193
2147
  */
1194
2148
  size() {
1195
- 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;
1196
2156
  }
1197
2157
  /**
1198
- * Get all keys in MindCache (including temporal keys).
2158
+ * Get all keys in MindCache.
1199
2159
  */
1200
2160
  keys() {
1201
- const keys = Array.from(this.rootMap.keys());
1202
- 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
+ }
1203
2167
  return keys;
1204
2168
  }
1205
2169
  /**
1206
- * Get all values in MindCache (including temporal values).
2170
+ * Get all values in MindCache.
1207
2171
  */
1208
2172
  values() {
1209
2173
  const result = [];
1210
2174
  for (const [key] of this.rootMap) {
1211
- result.push(this.get_value(key));
2175
+ if (this.keyMatchesContext(key)) {
2176
+ result.push(this.get_value(key));
2177
+ }
1212
2178
  }
1213
- result.push(this.get_value("$date"));
1214
- result.push(this.get_value("$time"));
1215
2179
  return result;
1216
2180
  }
1217
2181
  /**
1218
- * Get all key-value entries (including temporal entries).
2182
+ * Get all key-value entries.
1219
2183
  */
1220
2184
  entries() {
1221
2185
  const result = [];
1222
2186
  for (const [key] of this.rootMap) {
1223
- result.push([key, this.get_value(key)]);
2187
+ if (this.keyMatchesContext(key)) {
2188
+ result.push([key, this.get_value(key)]);
2189
+ }
1224
2190
  }
1225
- result.push(["$date", this.get_value("$date")]);
1226
- result.push(["$time", this.get_value("$time")]);
1227
2191
  return result;
1228
2192
  }
1229
2193
  /**
@@ -1244,7 +2208,6 @@ var MindCache = class {
1244
2208
  }
1245
2209
  /**
1246
2210
  * Get the STM as an object with values directly (no attributes).
1247
- * Includes system keys ($date, $time).
1248
2211
  * @deprecated Use getAll() for full STM format
1249
2212
  */
1250
2213
  getSTMObject() {
@@ -1252,8 +2215,6 @@ var MindCache = class {
1252
2215
  for (const [key] of this.rootMap) {
1253
2216
  result[key] = this.get_value(key);
1254
2217
  }
1255
- result["$date"] = this.get_value("$date");
1256
- result["$time"] = this.get_value("$time");
1257
2218
  return result;
1258
2219
  }
1259
2220
  /**
@@ -1261,479 +2222,132 @@ var MindCache = class {
1261
2222
  * @returns true if the tag was added, false if key doesn't exist or tag already exists
1262
2223
  */
1263
2224
  addTag(key, tag) {
1264
- if (key === "$date" || key === "$time" || key === "$version") {
1265
- return false;
1266
- }
1267
- const entryMap = this.rootMap.get(key);
1268
- if (!entryMap) {
1269
- return false;
1270
- }
1271
- const attributes = entryMap.get("attributes");
1272
- const contentTags = attributes?.contentTags || [];
1273
- if (contentTags.includes(tag)) {
1274
- return false;
1275
- }
1276
- this.doc.transact(() => {
1277
- const newContentTags = [...contentTags, tag];
1278
- entryMap.set("attributes", {
1279
- ...attributes,
1280
- contentTags: newContentTags,
1281
- tags: newContentTags
1282
- // Sync legacy tags array
1283
- });
1284
- });
1285
- this.notifyGlobalListeners();
1286
- return true;
2225
+ return TagManager.addTag(this, key, tag);
1287
2226
  }
1288
2227
  /**
1289
2228
  * Remove a content tag from a key.
1290
2229
  * @returns true if the tag was removed
1291
2230
  */
1292
2231
  removeTag(key, tag) {
1293
- if (key === "$date" || key === "$time" || key === "$version") {
1294
- return false;
1295
- }
1296
- const entryMap = this.rootMap.get(key);
1297
- if (!entryMap) {
1298
- return false;
1299
- }
1300
- const attributes = entryMap.get("attributes");
1301
- const contentTags = attributes?.contentTags || [];
1302
- const tagIndex = contentTags.indexOf(tag);
1303
- if (tagIndex === -1) {
1304
- return false;
1305
- }
1306
- this.doc.transact(() => {
1307
- const newContentTags = contentTags.filter((t) => t !== tag);
1308
- entryMap.set("attributes", {
1309
- ...attributes,
1310
- contentTags: newContentTags,
1311
- tags: newContentTags
1312
- // Sync legacy tags array
1313
- });
1314
- });
1315
- this.notifyGlobalListeners();
1316
- return true;
2232
+ return TagManager.removeTag(this, key, tag);
1317
2233
  }
1318
2234
  /**
1319
2235
  * Get all content tags for a key.
1320
2236
  */
1321
2237
  getTags(key) {
1322
- if (key === "$date" || key === "$time" || key === "$version") {
1323
- return [];
1324
- }
1325
- const entryMap = this.rootMap.get(key);
1326
- if (!entryMap) {
1327
- return [];
1328
- }
1329
- const attributes = entryMap.get("attributes");
1330
- return attributes?.contentTags || [];
2238
+ return TagManager.getTags(this, key);
1331
2239
  }
1332
2240
  /**
1333
2241
  * Get all unique content tags across all keys.
1334
2242
  */
1335
2243
  getAllTags() {
1336
- const allTags = /* @__PURE__ */ new Set();
1337
- for (const [, val] of this.rootMap) {
1338
- const entryMap = val;
1339
- const attributes = entryMap.get("attributes");
1340
- if (attributes?.contentTags) {
1341
- attributes.contentTags.forEach((tag) => allTags.add(tag));
1342
- }
1343
- }
1344
- return Array.from(allTags);
2244
+ return TagManager.getAllTags(this);
1345
2245
  }
1346
2246
  /**
1347
2247
  * Check if a key has a specific content tag.
1348
2248
  */
1349
2249
  hasTag(key, tag) {
1350
- if (key === "$date" || key === "$time" || key === "$version") {
1351
- return false;
1352
- }
1353
- const entryMap = this.rootMap.get(key);
1354
- if (!entryMap) {
1355
- return false;
1356
- }
1357
- const attributes = entryMap.get("attributes");
1358
- return attributes?.contentTags?.includes(tag) || false;
2250
+ return TagManager.hasTag(this, key, tag);
1359
2251
  }
1360
2252
  /**
1361
2253
  * Get all keys with a specific content tag as formatted string.
1362
2254
  */
1363
2255
  getTagged(tag) {
1364
- const entries = [];
1365
- const keys = this.getSortedKeys();
1366
- keys.forEach((key) => {
1367
- if (this.hasTag(key, tag)) {
1368
- entries.push([key, this.get_value(key)]);
1369
- }
1370
- });
1371
- return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
2256
+ return TagManager.getTagged(this, tag);
1372
2257
  }
1373
- /**
1374
- * Get array of keys with a specific content tag.
1375
- */
1376
- getKeysByTag(tag) {
1377
- const keys = this.getSortedKeys();
1378
- return keys.filter((key) => this.hasTag(key, tag));
1379
- }
1380
- // ============================================
1381
- // System Tag Methods (requires system access level)
1382
- // ============================================
1383
- /**
1384
- * Add a system tag to a key (requires system access).
1385
- * System tags: 'SystemPrompt', 'LLMRead', 'LLMWrite', 'readonly', 'protected', 'ApplyTemplate'
1386
- */
1387
- systemAddTag(key, tag) {
1388
- if (!this.hasSystemAccess) {
1389
- console.warn("MindCache: systemAddTag requires system access level");
1390
- return false;
1391
- }
1392
- if (key === "$date" || key === "$time" || key === "$version") {
1393
- return false;
1394
- }
1395
- const entryMap = this.rootMap.get(key);
1396
- if (!entryMap) {
1397
- return false;
1398
- }
1399
- const attributes = entryMap.get("attributes");
1400
- const systemTags = attributes?.systemTags || [];
1401
- if (systemTags.includes(tag)) {
1402
- return false;
1403
- }
1404
- this.doc.transact(() => {
1405
- const newSystemTags = [...systemTags, tag];
1406
- const normalizedTags = this.normalizeSystemTags(newSystemTags);
1407
- entryMap.set("attributes", {
1408
- ...attributes,
1409
- systemTags: normalizedTags
1410
- });
1411
- });
1412
- this.notifyGlobalListeners();
1413
- return true;
1414
- }
1415
- /**
1416
- * Remove a system tag from a key (requires system access).
1417
- */
1418
- systemRemoveTag(key, tag) {
1419
- if (!this.hasSystemAccess) {
1420
- console.warn("MindCache: systemRemoveTag requires system access level");
1421
- return false;
1422
- }
1423
- if (key === "$date" || key === "$time" || key === "$version") {
1424
- return false;
1425
- }
1426
- const entryMap = this.rootMap.get(key);
1427
- if (!entryMap) {
1428
- return false;
1429
- }
1430
- const attributes = entryMap.get("attributes");
1431
- const systemTags = attributes?.systemTags || [];
1432
- const tagIndex = systemTags.indexOf(tag);
1433
- if (tagIndex === -1) {
1434
- return false;
1435
- }
1436
- this.doc.transact(() => {
1437
- const newSystemTags = systemTags.filter((t) => t !== tag);
1438
- entryMap.set("attributes", {
1439
- ...attributes,
1440
- systemTags: newSystemTags
1441
- });
1442
- });
1443
- this.notifyGlobalListeners();
1444
- return true;
2258
+ /**
2259
+ * Get array of keys with a specific content tag.
2260
+ */
2261
+ getKeysByTag(tag) {
2262
+ return TagManager.getKeysByTag(this, tag);
2263
+ }
2264
+ // ============================================
2265
+ // System Tag Methods (requires system access level)
2266
+ // ============================================
2267
+ /**
2268
+ * Add a system tag to a key (requires system access).
2269
+ * System tags: 'SystemPrompt', 'LLMRead', 'LLMWrite', 'ApplyTemplate'
2270
+ */
2271
+ systemAddTag(key, tag) {
2272
+ return TagManager.systemAddTag(this, key, tag);
2273
+ }
2274
+ /**
2275
+ * Remove a system tag from a key (requires system access).
2276
+ */
2277
+ systemRemoveTag(key, tag) {
2278
+ return TagManager.systemRemoveTag(this, key, tag);
1445
2279
  }
1446
2280
  /**
1447
2281
  * Get all system tags for a key (requires system access).
1448
2282
  */
1449
2283
  systemGetTags(key) {
1450
- if (!this.hasSystemAccess) {
1451
- console.warn("MindCache: systemGetTags requires system access level");
1452
- return [];
1453
- }
1454
- if (key === "$date" || key === "$time" || key === "$version") {
1455
- return [];
1456
- }
1457
- const entryMap = this.rootMap.get(key);
1458
- if (!entryMap) {
1459
- return [];
1460
- }
1461
- const attributes = entryMap.get("attributes");
1462
- return attributes?.systemTags || [];
2284
+ return TagManager.systemGetTags(this, key);
1463
2285
  }
1464
2286
  /**
1465
2287
  * Check if a key has a specific system tag (requires system access).
1466
2288
  */
1467
2289
  systemHasTag(key, tag) {
1468
- if (!this.hasSystemAccess) {
1469
- console.warn("MindCache: systemHasTag requires system access level");
1470
- return false;
1471
- }
1472
- if (key === "$date" || key === "$time" || key === "$version") {
1473
- return false;
1474
- }
1475
- const entryMap = this.rootMap.get(key);
1476
- if (!entryMap) {
1477
- return false;
1478
- }
1479
- const attributes = entryMap.get("attributes");
1480
- return attributes?.systemTags?.includes(tag) || false;
2290
+ return TagManager.systemHasTag(this, key, tag);
1481
2291
  }
1482
2292
  /**
1483
2293
  * Set all system tags for a key at once (requires system access).
1484
2294
  */
1485
2295
  systemSetTags(key, tags) {
1486
- if (!this.hasSystemAccess) {
1487
- console.warn("MindCache: systemSetTags requires system access level");
1488
- return false;
1489
- }
1490
- if (key === "$date" || key === "$time" || key === "$version") {
1491
- return false;
1492
- }
1493
- const entryMap = this.rootMap.get(key);
1494
- if (!entryMap) {
1495
- return false;
1496
- }
1497
- this.doc.transact(() => {
1498
- const attributes = entryMap.get("attributes");
1499
- entryMap.set("attributes", {
1500
- ...attributes,
1501
- systemTags: [...tags]
1502
- });
1503
- });
1504
- this.notifyGlobalListeners();
1505
- return true;
2296
+ return TagManager.systemSetTags(this, key, tags);
1506
2297
  }
1507
2298
  /**
1508
2299
  * Get all keys with a specific system tag (requires system access).
1509
2300
  */
1510
2301
  systemGetKeysByTag(tag) {
1511
- if (!this.hasSystemAccess) {
1512
- console.warn("MindCache: systemGetKeysByTag requires system access level");
1513
- return [];
1514
- }
1515
- const keys = this.getSortedKeys();
1516
- return keys.filter((key) => this.systemHasTag(key, tag));
2302
+ return TagManager.systemGetKeysByTag(this, tag);
1517
2303
  }
1518
2304
  /**
1519
2305
  * Helper to get sorted keys (by zIndex).
2306
+ * Respects context filtering when set.
1520
2307
  */
1521
2308
  getSortedKeys() {
1522
2309
  const entries = [];
1523
2310
  for (const [key, val] of this.rootMap) {
1524
- const entryMap = val;
1525
- const attributes = entryMap.get("attributes");
1526
- entries.push({ key, zIndex: attributes?.zIndex ?? 0 });
1527
- }
1528
- return entries.sort((a, b) => a.zIndex - b.zIndex).map((e) => e.key);
1529
- }
1530
- /**
1531
- * Serialize to JSON string.
1532
- */
1533
- toJSON() {
1534
- return JSON.stringify(this.serialize());
1535
- }
1536
- /**
1537
- * Deserialize from JSON string.
1538
- */
1539
- fromJSON(jsonString) {
1540
- try {
1541
- const data = JSON.parse(jsonString);
1542
- this.deserialize(data);
1543
- } catch (error) {
1544
- console.error("MindCache: Failed to deserialize JSON:", error);
1545
- }
1546
- }
1547
- /**
1548
- * Export to Markdown format.
1549
- */
1550
- toMarkdown() {
1551
- const now = /* @__PURE__ */ new Date();
1552
- const lines = [];
1553
- const appendixEntries = [];
1554
- let appendixCounter = 0;
1555
- lines.push("# MindCache STM Export");
1556
- lines.push("");
1557
- lines.push(`Export Date: ${now.toISOString().split("T")[0]}`);
1558
- lines.push("");
1559
- lines.push("---");
1560
- lines.push("");
1561
- lines.push("## STM Entries");
1562
- lines.push("");
1563
- const sortedKeys = this.getSortedKeys();
1564
- sortedKeys.forEach((key) => {
1565
- const entryMap = this.rootMap.get(key);
1566
- if (!entryMap) {
1567
- return;
1568
- }
1569
- const attributes = entryMap.get("attributes");
1570
- const value = entryMap.get("value");
1571
- if (attributes?.systemTags?.includes("protected")) {
1572
- return;
1573
- }
1574
- lines.push(`### ${key}`);
1575
- const entryType = attributes?.type || "text";
1576
- lines.push(`- **Type**: \`${entryType}\``);
1577
- lines.push(`- **System Tags**: \`${attributes?.systemTags?.join(", ") || "none"}\``);
1578
- lines.push(`- **Z-Index**: \`${attributes?.zIndex ?? 0}\``);
1579
- if (attributes?.contentTags && attributes.contentTags.length > 0) {
1580
- lines.push(`- **Tags**: \`${attributes.contentTags.join("`, `")}\``);
1581
- }
1582
- if (attributes?.contentType) {
1583
- lines.push(`- **Content Type**: \`${attributes.contentType}\``);
1584
- }
1585
- if (entryType === "image" || entryType === "file") {
1586
- const label = String.fromCharCode(65 + appendixCounter);
1587
- appendixCounter++;
1588
- lines.push(`- **Value**: [See Appendix ${label}]`);
1589
- appendixEntries.push({
1590
- key,
1591
- type: entryType,
1592
- contentType: attributes?.contentType || "application/octet-stream",
1593
- base64: value,
1594
- label
1595
- });
1596
- } else if (entryType === "json") {
1597
- lines.push("- **Value**:");
1598
- lines.push("```json");
1599
- try {
1600
- const jsonValue = typeof value === "string" ? value : JSON.stringify(value, null, 2);
1601
- lines.push(jsonValue);
1602
- } catch {
1603
- lines.push(String(value));
1604
- }
1605
- lines.push("```");
1606
- } else {
1607
- lines.push("- **Value**:");
1608
- lines.push("```");
1609
- lines.push(String(value));
1610
- lines.push("```");
1611
- }
1612
- lines.push("");
1613
- });
1614
- if (appendixEntries.length > 0) {
1615
- lines.push("---");
1616
- lines.push("");
1617
- lines.push("## Appendix: Binary Data");
1618
- lines.push("");
1619
- appendixEntries.forEach((entry) => {
1620
- lines.push(`### Appendix ${entry.label}: ${entry.key}`);
1621
- lines.push(`- **Type**: \`${entry.type}\``);
1622
- lines.push(`- **Content Type**: \`${entry.contentType}\``);
1623
- lines.push("- **Base64 Data**:");
1624
- lines.push("```");
1625
- lines.push(entry.base64);
1626
- lines.push("```");
1627
- lines.push("");
1628
- });
1629
- }
1630
- return lines.join("\n");
1631
- }
1632
- /**
1633
- * Import from Markdown format.
1634
- */
1635
- fromMarkdown(markdown) {
1636
- const lines = markdown.split("\n");
1637
- let currentKey = null;
1638
- let currentAttributes = {};
1639
- let currentValue = null;
1640
- let inCodeBlock = false;
1641
- let codeBlockContent = [];
1642
- for (const line of lines) {
1643
- if (line.startsWith("### ") && !line.startsWith("### Appendix")) {
1644
- if (currentKey && currentValue !== null) {
1645
- this.set_value(currentKey, currentValue.trim(), currentAttributes);
1646
- }
1647
- currentKey = line.substring(4).trim();
1648
- currentAttributes = {};
1649
- currentValue = null;
1650
- continue;
1651
- }
1652
- if (line.startsWith("### Appendix ")) {
1653
- const match = line.match(/### Appendix ([A-Z]): (.+)/);
1654
- if (match) {
1655
- currentKey = match[2];
1656
- }
1657
- continue;
1658
- }
1659
- if (line.startsWith("- **Type**:")) {
1660
- const type = line.match(/`(.+)`/)?.[1];
1661
- if (type) {
1662
- currentAttributes.type = type;
1663
- }
1664
- continue;
1665
- }
1666
- if (line.startsWith("- **System Tags**:")) {
1667
- const tagsStr = line.match(/`([^`]+)`/)?.[1] || "";
1668
- if (tagsStr !== "none") {
1669
- currentAttributes.systemTags = tagsStr.split(", ").filter((t) => t);
1670
- }
1671
- continue;
1672
- }
1673
- if (line.startsWith("- **Z-Index**:")) {
1674
- const zIndex = parseInt(line.match(/`(\d+)`/)?.[1] || "0", 10);
1675
- currentAttributes.zIndex = zIndex;
1676
- continue;
1677
- }
1678
- if (line.startsWith("- **Tags**:")) {
1679
- const tags = line.match(/`([^`]+)`/g)?.map((t) => t.slice(1, -1)) || [];
1680
- currentAttributes.contentTags = tags;
1681
- continue;
1682
- }
1683
- if (line.startsWith("- **Content Type**:")) {
1684
- currentAttributes.contentType = line.match(/`(.+)`/)?.[1];
1685
- continue;
1686
- }
1687
- if (line.startsWith("- **Value**:") && !line.includes("[See Appendix")) {
1688
- const afterValue = line.substring(12).trim();
1689
- if (afterValue === "") {
1690
- currentValue = "";
1691
- } else if (afterValue === "```" || afterValue === "```json") {
1692
- inCodeBlock = true;
1693
- codeBlockContent = [];
1694
- currentValue = "";
1695
- } else if (afterValue.startsWith("```")) {
1696
- inCodeBlock = true;
1697
- codeBlockContent = [afterValue.substring(3)];
1698
- currentValue = "";
1699
- } else {
1700
- currentValue = afterValue;
1701
- }
1702
- continue;
1703
- }
1704
- const trimmedLine = line.trim();
1705
- if (trimmedLine === "```json" || trimmedLine === "```") {
1706
- if (inCodeBlock) {
1707
- inCodeBlock = false;
1708
- if (currentKey && codeBlockContent.length > 0) {
1709
- currentValue = codeBlockContent.join("\n");
1710
- }
1711
- codeBlockContent = [];
1712
- } else {
1713
- inCodeBlock = true;
1714
- codeBlockContent = [];
1715
- }
2311
+ if (!this.keyMatchesContext(key)) {
1716
2312
  continue;
1717
- }
1718
- if (inCodeBlock) {
1719
- codeBlockContent.push(line);
1720
- } else if (currentKey && currentValue !== null) {
1721
- currentValue += "\n" + line;
1722
- }
1723
- }
1724
- if (currentKey && currentValue !== null) {
1725
- this.set_value(currentKey, currentValue.trim(), currentAttributes);
1726
- }
1727
- const hasParsedKeys = lines.some((line) => line.startsWith("### ") && !line.startsWith("### Appendix"));
1728
- if (!hasParsedKeys && markdown.trim().length > 0) {
1729
- this.set_value("imported_content", markdown.trim(), {
1730
- type: "text",
1731
- systemTags: ["SystemPrompt", "LLMWrite"],
1732
- // Default assumptions
1733
- zIndex: 0
1734
- });
2313
+ }
2314
+ const entryMap = val;
2315
+ const attributes = entryMap.get("attributes");
2316
+ entries.push({ key, zIndex: attributes?.zIndex ?? 0 });
2317
+ }
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);
1735
2335
  }
1736
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
+ }
1737
2351
  /**
1738
2352
  * Set base64 binary data.
1739
2353
  */
@@ -1814,9 +2428,6 @@ var MindCache = class {
1814
2428
  * Note: This exposes Yjs Y.Text directly for editor bindings (y-quill, y-codemirror, etc.)
1815
2429
  */
1816
2430
  set_document(key, initialText, attributes) {
1817
- if (key === "$date" || key === "$time" || key === "$version") {
1818
- return;
1819
- }
1820
2431
  let entryMap = this.rootMap.get(key);
1821
2432
  if (!entryMap) {
1822
2433
  entryMap = new Y.Map();
@@ -1875,7 +2486,7 @@ var MindCache = class {
1875
2486
  }
1876
2487
  }
1877
2488
  /**
1878
- * Replace all text in a document key (private - use set_value for public API).
2489
+ * Replace all text in a document key.
1879
2490
  * Uses diff-based updates when changes are < diffThreshold (default 80%).
1880
2491
  * This preserves concurrent edits and provides better undo granularity.
1881
2492
  */
@@ -1945,16 +2556,11 @@ var MindCache = class {
1945
2556
  }
1946
2557
  // Sanitize key name for use in tool names
1947
2558
  sanitizeKeyForTool(key) {
1948
- return key.replace(/[^a-zA-Z0-9_-]/g, "_");
2559
+ return AIToolBuilder.sanitizeKeyForTool(key);
1949
2560
  }
1950
2561
  // Find original key from sanitized tool name
1951
2562
  findKeyFromSanitizedTool(sanitizedKey) {
1952
- for (const [key] of this.rootMap) {
1953
- if (this.sanitizeKeyForTool(key) === sanitizedKey) {
1954
- return key;
1955
- }
1956
- }
1957
- return void 0;
2563
+ return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
1958
2564
  }
1959
2565
  /**
1960
2566
  * Generate Vercel AI SDK compatible tools for writable keys.
@@ -1965,130 +2571,7 @@ var MindCache = class {
1965
2571
  * - Prevents LLMs from escalating privileges
1966
2572
  */
1967
2573
  create_vercel_ai_tools() {
1968
- const tools = {};
1969
- for (const [key, val] of this.rootMap) {
1970
- if (key.startsWith("$")) {
1971
- continue;
1972
- }
1973
- const entryMap = val;
1974
- const attributes = entryMap.get("attributes");
1975
- const isWritable = attributes?.systemTags?.includes("LLMWrite");
1976
- if (!isWritable) {
1977
- continue;
1978
- }
1979
- const sanitizedKey = this.sanitizeKeyForTool(key);
1980
- const isDocument = attributes?.type === "document";
1981
- tools[`write_${sanitizedKey}`] = {
1982
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
1983
- inputSchema: {
1984
- type: "object",
1985
- properties: {
1986
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1987
- },
1988
- required: ["value"]
1989
- },
1990
- execute: async ({ value }) => {
1991
- const success = this.llm_set_key(key, value);
1992
- if (success) {
1993
- return {
1994
- result: `Successfully wrote "${value}" to ${key}`,
1995
- key,
1996
- value
1997
- };
1998
- }
1999
- return {
2000
- result: `Failed to write to ${key} - permission denied or key not found`,
2001
- key,
2002
- error: true
2003
- };
2004
- }
2005
- };
2006
- if (isDocument) {
2007
- tools[`append_${sanitizedKey}`] = {
2008
- description: `Append text to the end of "${key}" document`,
2009
- inputSchema: {
2010
- type: "object",
2011
- properties: {
2012
- text: { type: "string", description: "Text to append" }
2013
- },
2014
- required: ["text"]
2015
- },
2016
- execute: async ({ text }) => {
2017
- if (!attributes?.systemTags?.includes("LLMWrite")) {
2018
- return { result: `Permission denied for ${key}`, key, error: true };
2019
- }
2020
- const yText = this.get_document(key);
2021
- if (yText) {
2022
- yText.insert(yText.length, text);
2023
- return {
2024
- result: `Successfully appended to ${key}`,
2025
- key,
2026
- appended: text
2027
- };
2028
- }
2029
- return { result: `Document ${key} not found`, key };
2030
- }
2031
- };
2032
- tools[`insert_${sanitizedKey}`] = {
2033
- description: `Insert text at a position in "${key}" document`,
2034
- inputSchema: {
2035
- type: "object",
2036
- properties: {
2037
- index: { type: "number", description: "Position to insert at (0 = start)" },
2038
- text: { type: "string", description: "Text to insert" }
2039
- },
2040
- required: ["index", "text"]
2041
- },
2042
- execute: async ({ index, text }) => {
2043
- if (!attributes?.systemTags?.includes("LLMWrite")) {
2044
- return { result: `Permission denied for ${key}`, key, error: true };
2045
- }
2046
- this.insert_text(key, index, text);
2047
- return {
2048
- result: `Successfully inserted text at position ${index} in ${key}`,
2049
- key,
2050
- index,
2051
- inserted: text
2052
- };
2053
- }
2054
- };
2055
- tools[`edit_${sanitizedKey}`] = {
2056
- description: `Find and replace text in "${key}" document`,
2057
- inputSchema: {
2058
- type: "object",
2059
- properties: {
2060
- find: { type: "string", description: "Text to find" },
2061
- replace: { type: "string", description: "Replacement text" }
2062
- },
2063
- required: ["find", "replace"]
2064
- },
2065
- execute: async ({ find, replace }) => {
2066
- if (!attributes?.systemTags?.includes("LLMWrite")) {
2067
- return { result: `Permission denied for ${key}`, key, error: true };
2068
- }
2069
- const yText = this.get_document(key);
2070
- if (yText) {
2071
- const text = yText.toString();
2072
- const idx = text.indexOf(find);
2073
- if (idx !== -1) {
2074
- yText.delete(idx, find.length);
2075
- yText.insert(idx, replace);
2076
- return {
2077
- result: `Successfully replaced "${find}" with "${replace}" in ${key}`,
2078
- key,
2079
- find,
2080
- replace,
2081
- index: idx
2082
- };
2083
- }
2084
- return { result: `Text "${find}" not found in ${key}`, key };
2085
- }
2086
- return { result: `Document ${key} not found`, key };
2087
- }
2088
- };
2089
- }
2090
- }
2091
- return tools;
2574
+ return AIToolBuilder.createVercelAITools(this);
2092
2575
  }
2093
2576
  /**
2094
2577
  * @deprecated Use create_vercel_ai_tools() instead
@@ -2101,121 +2584,14 @@ var MindCache = class {
2101
2584
  * Indicates which tools can be used to modify writable keys.
2102
2585
  */
2103
2586
  get_system_prompt() {
2104
- const lines = [];
2105
- for (const [key, val] of this.rootMap) {
2106
- if (key.startsWith("$")) {
2107
- continue;
2108
- }
2109
- const entryMap = val;
2110
- const attributes = entryMap.get("attributes");
2111
- const isVisible = attributes?.systemTags?.includes("SystemPrompt") || attributes?.systemTags?.includes("LLMRead");
2112
- if (!isVisible) {
2113
- continue;
2114
- }
2115
- const value = this.get_value(key);
2116
- const displayValue = typeof value === "object" ? JSON.stringify(value) : value;
2117
- const isWritable = attributes?.systemTags?.includes("LLMWrite");
2118
- const isDocument = attributes?.type === "document";
2119
- const sanitizedKey = this.sanitizeKeyForTool(key);
2120
- if (isWritable) {
2121
- if (isDocument) {
2122
- lines.push(
2123
- `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
2124
- );
2125
- } else {
2126
- const oldValueHint = displayValue ? ` This tool DOES NOT append \u2014 start your response with the old value (${displayValue})` : "";
2127
- lines.push(
2128
- `${key}: ${displayValue}. You can rewrite "${key}" by using the write_${sanitizedKey} tool.${oldValueHint}`
2129
- );
2130
- }
2131
- } else {
2132
- lines.push(`${key}: ${displayValue}`);
2133
- }
2134
- }
2135
- lines.push(`$date: ${this.get_value("$date")}`);
2136
- lines.push(`$time: ${this.get_value("$time")}`);
2137
- return lines.join(", ");
2587
+ return AIToolBuilder.getSystemPrompt(this);
2138
2588
  }
2139
2589
  /**
2140
2590
  * Execute a tool call by name with the given value.
2141
2591
  * Returns the result or null if tool not found.
2142
2592
  */
2143
2593
  executeToolCall(toolName, value) {
2144
- const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
2145
- if (!match) {
2146
- return null;
2147
- }
2148
- const [, action, sanitizedKey] = match;
2149
- const key = this.findKeyFromSanitizedTool(sanitizedKey);
2150
- if (!key) {
2151
- return null;
2152
- }
2153
- const entryMap = this.rootMap.get(key);
2154
- if (!entryMap) {
2155
- return null;
2156
- }
2157
- const attributes = entryMap.get("attributes");
2158
- const isWritable = attributes?.systemTags?.includes("LLMWrite");
2159
- if (!isWritable) {
2160
- return null;
2161
- }
2162
- const isDocument = attributes?.type === "document";
2163
- switch (action) {
2164
- case "write":
2165
- if (isDocument) {
2166
- this._replaceDocumentText(key, value);
2167
- } else {
2168
- this.set_value(key, value);
2169
- }
2170
- return {
2171
- result: `Successfully wrote "${value}" to ${key}`,
2172
- key,
2173
- value
2174
- };
2175
- case "append":
2176
- if (isDocument) {
2177
- const yText = this.get_document(key);
2178
- if (yText) {
2179
- yText.insert(yText.length, value);
2180
- return {
2181
- result: `Successfully appended to ${key}`,
2182
- key,
2183
- value
2184
- };
2185
- }
2186
- }
2187
- return null;
2188
- case "insert":
2189
- if (isDocument && typeof value === "object" && value.index !== void 0 && value.text) {
2190
- this.insert_text(key, value.index, value.text);
2191
- return {
2192
- result: `Successfully inserted at position ${value.index} in ${key}`,
2193
- key,
2194
- value: value.text
2195
- };
2196
- }
2197
- return null;
2198
- case "edit":
2199
- if (isDocument && typeof value === "object" && value.find && value.replace !== void 0) {
2200
- const yText = this.get_document(key);
2201
- if (yText) {
2202
- const text = yText.toString();
2203
- const idx = text.indexOf(value.find);
2204
- if (idx !== -1) {
2205
- yText.delete(idx, value.find.length);
2206
- yText.insert(idx, value.replace);
2207
- return {
2208
- result: `Successfully replaced "${value.find}" with "${value.replace}" in ${key}`,
2209
- key,
2210
- value: value.replace
2211
- };
2212
- }
2213
- }
2214
- }
2215
- return null;
2216
- default:
2217
- return null;
2218
- }
2594
+ return AIToolBuilder.executeToolCall(this, toolName, value);
2219
2595
  }
2220
2596
  // Internal method stub for legacy compatibility
2221
2597
  _setFromRemote(_key, _value, _attributes) {