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