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/README.md +22 -0
- package/dist/{CloudAdapter-CJS3Sh4f.d.mts → CloudAdapter-CM7nyJaG.d.mts} +85 -25
- package/dist/{CloudAdapter-CJS3Sh4f.d.ts → CloudAdapter-CM7nyJaG.d.ts} +85 -25
- package/dist/cloud/index.d.mts +2 -2
- package/dist/cloud/index.d.ts +2 -2
- package/dist/cloud/index.js +1105 -729
- package/dist/cloud/index.js.map +1 -1
- package/dist/cloud/index.mjs +1105 -729
- package/dist/cloud/index.mjs.map +1 -1
- package/dist/index.d.mts +125 -3
- package/dist/index.d.ts +125 -3
- package/dist/index.js +1527 -832
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1526 -833
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1105 -731
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +1105 -731
- package/dist/server.mjs.map +1 -1
- package/docs/mindcache-api.md +148 -0
- package/package.json +3 -2
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", "
|
|
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
|
|
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 === "
|
|
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
|
|
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
|
|
975
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
|
1187
|
-
return
|
|
2143
|
+
if (!this.rootMap.has(key)) {
|
|
2144
|
+
return false;
|
|
1188
2145
|
}
|
|
1189
|
-
return this.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2196
|
+
* Get all keys in MindCache.
|
|
1239
2197
|
*/
|
|
1240
2198
|
keys() {
|
|
1241
|
-
const keys =
|
|
1242
|
-
|
|
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
|
|
2208
|
+
* Get all values in MindCache.
|
|
1247
2209
|
*/
|
|
1248
2210
|
values() {
|
|
1249
2211
|
const result = [];
|
|
1250
2212
|
for (const [key] of this.rootMap) {
|
|
1251
|
-
|
|
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
|
|
2220
|
+
* Get all key-value entries.
|
|
1259
2221
|
*/
|
|
1260
2222
|
entries() {
|
|
1261
2223
|
const result = [];
|
|
1262
2224
|
for (const [key] of this.rootMap) {
|
|
1263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
//
|
|
1421
|
-
//
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
*
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
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
|
|
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
|
|
2597
|
+
return AIToolBuilder.sanitizeKeyForTool(key);
|
|
1989
2598
|
}
|
|
1990
2599
|
// Find original key from sanitized tool name
|
|
1991
2600
|
findKeyFromSanitizedTool(sanitizedKey) {
|
|
1992
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|