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