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