mindcache 3.4.3 → 3.5.0

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