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