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