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