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