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