mindcache 2.2.0 → 2.4.0

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.
@@ -10,21 +10,173 @@ var __export = (target, all) => {
10
10
  __defProp(target, name, { get: all[name], enumerable: true });
11
11
  };
12
12
 
13
+ // src/local/IndexedDBAdapter.ts
14
+ var IndexedDBAdapter_exports = {};
15
+ __export(IndexedDBAdapter_exports, {
16
+ IndexedDBAdapter: () => IndexedDBAdapter
17
+ });
18
+ var IndexedDBAdapter;
19
+ var init_IndexedDBAdapter = __esm({
20
+ "src/local/IndexedDBAdapter.ts"() {
21
+ IndexedDBAdapter = class {
22
+ constructor(config = {}) {
23
+ this.config = config;
24
+ this.dbName = config.dbName || "mindcache_db";
25
+ this.storeName = config.storeName || "mindcache_store";
26
+ this.key = config.key || "mindcache_data";
27
+ }
28
+ mindcache = null;
29
+ unsubscribe = null;
30
+ saveTimeout = null;
31
+ db = null;
32
+ dbName;
33
+ storeName;
34
+ key;
35
+ async attach(mc) {
36
+ if (this.mindcache) {
37
+ this.detach();
38
+ }
39
+ this.mindcache = mc;
40
+ await this.initDB();
41
+ await this.load();
42
+ const listener = () => {
43
+ if (this.mindcache && !this.mindcache.isRemoteUpdate()) {
44
+ this.scheduleSave();
45
+ }
46
+ };
47
+ mc.subscribeToAll(listener);
48
+ this.unsubscribe = () => mc.unsubscribeFromAll(listener);
49
+ console.log("\u{1F5C4}\uFE0F IndexedDBAdapter: Attached to MindCache instance");
50
+ }
51
+ detach() {
52
+ if (this.unsubscribe) {
53
+ this.unsubscribe();
54
+ this.unsubscribe = null;
55
+ }
56
+ if (this.saveTimeout) {
57
+ clearTimeout(this.saveTimeout);
58
+ this.saveTimeout = null;
59
+ }
60
+ this.mindcache = null;
61
+ if (this.db) {
62
+ this.db.close();
63
+ this.db = null;
64
+ }
65
+ }
66
+ initDB() {
67
+ return new Promise((resolve, reject) => {
68
+ const request = indexedDB.open(this.dbName);
69
+ request.onerror = () => {
70
+ console.error("MindCache: IndexedDB error:", request.error);
71
+ reject(request.error);
72
+ };
73
+ request.onsuccess = () => {
74
+ const db = request.result;
75
+ if (!db.objectStoreNames.contains(this.storeName)) {
76
+ const currentVersion = db.version;
77
+ db.close();
78
+ const upgradeRequest = indexedDB.open(this.dbName, currentVersion + 1);
79
+ upgradeRequest.onerror = () => {
80
+ console.error("MindCache: IndexedDB upgrade error:", upgradeRequest.error);
81
+ reject(upgradeRequest.error);
82
+ };
83
+ upgradeRequest.onupgradeneeded = () => {
84
+ const upgradeDb = upgradeRequest.result;
85
+ if (!upgradeDb.objectStoreNames.contains(this.storeName)) {
86
+ upgradeDb.createObjectStore(this.storeName);
87
+ }
88
+ };
89
+ upgradeRequest.onsuccess = () => {
90
+ this.db = upgradeRequest.result;
91
+ resolve();
92
+ };
93
+ } else {
94
+ this.db = db;
95
+ resolve();
96
+ }
97
+ };
98
+ request.onupgradeneeded = () => {
99
+ const db = request.result;
100
+ if (!db.objectStoreNames.contains(this.storeName)) {
101
+ db.createObjectStore(this.storeName);
102
+ }
103
+ };
104
+ });
105
+ }
106
+ load() {
107
+ if (!this.db || !this.mindcache) {
108
+ return Promise.resolve();
109
+ }
110
+ return new Promise((resolve) => {
111
+ try {
112
+ const transaction = this.db.transaction([this.storeName], "readonly");
113
+ const store = transaction.objectStore(this.storeName);
114
+ const request = store.get(this.key);
115
+ request.onsuccess = () => {
116
+ if (request.result) {
117
+ this.mindcache.deserialize(request.result);
118
+ console.log("\u{1F5C4}\uFE0F IndexedDBAdapter: Loaded data from IndexedDB");
119
+ }
120
+ resolve();
121
+ };
122
+ request.onerror = () => {
123
+ console.error("MindCache: Failed to load from IndexedDB:", request.error);
124
+ resolve();
125
+ };
126
+ } catch (error) {
127
+ console.error("MindCache: Error accessing IndexedDB for load:", error);
128
+ resolve();
129
+ }
130
+ });
131
+ }
132
+ scheduleSave() {
133
+ if (this.saveTimeout) {
134
+ clearTimeout(this.saveTimeout);
135
+ }
136
+ this.saveTimeout = setTimeout(() => {
137
+ this.save();
138
+ this.saveTimeout = null;
139
+ }, this.config.debounceMs ?? 1e3);
140
+ }
141
+ save() {
142
+ if (!this.db || !this.mindcache) {
143
+ return;
144
+ }
145
+ try {
146
+ const data = this.mindcache.serialize();
147
+ const transaction = this.db.transaction([this.storeName], "readwrite");
148
+ const store = transaction.objectStore(this.storeName);
149
+ const request = store.put(data, this.key);
150
+ request.onsuccess = () => {
151
+ console.log("\u{1F5C4}\uFE0F IndexedDBAdapter: Saved to IndexedDB");
152
+ };
153
+ request.onerror = () => {
154
+ console.error("MindCache: Failed to save to IndexedDB:", request.error);
155
+ };
156
+ } catch (error) {
157
+ console.error("MindCache: Error accessing IndexedDB for save:", error);
158
+ }
159
+ }
160
+ };
161
+ }
162
+ });
163
+
13
164
  // src/cloud/CloudAdapter.ts
14
165
  var CloudAdapter_exports = {};
15
166
  __export(CloudAdapter_exports, {
16
167
  CloudAdapter: () => CloudAdapter
17
168
  });
18
- var DEFAULT_BASE_URL, RECONNECT_DELAY, MAX_RECONNECT_DELAY, CloudAdapter;
169
+ var RECONNECT_DELAY, MAX_RECONNECT_DELAY, CloudAdapter;
19
170
  var init_CloudAdapter = __esm({
20
171
  "src/cloud/CloudAdapter.ts"() {
21
- DEFAULT_BASE_URL = "wss://api.mindcache.io";
22
172
  RECONNECT_DELAY = 1e3;
23
173
  MAX_RECONNECT_DELAY = 3e4;
24
174
  CloudAdapter = class {
25
175
  constructor(config) {
26
176
  this.config = config;
27
- this.config.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
177
+ if (!config.baseUrl) {
178
+ throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
179
+ }
28
180
  }
29
181
  ws = null;
30
182
  queue = [];
@@ -85,6 +237,40 @@ var init_CloudAdapter = __esm({
85
237
  }
86
238
  this.mindcache = null;
87
239
  }
240
+ /**
241
+ * Fetch a short-lived WebSocket token from the API using the API key.
242
+ * This keeps the API key secure by only using it for a single HTTPS request,
243
+ * then using the short-lived token for the WebSocket connection.
244
+ *
245
+ * Supports two key formats:
246
+ * - API keys: mc_live_xxx or mc_test_xxx → Bearer token
247
+ * - Delegate keys: del_xxx:sec_xxx → ApiKey format
248
+ */
249
+ async fetchTokenWithApiKey() {
250
+ if (!this.config.apiKey) {
251
+ throw new Error("API key is required to fetch token");
252
+ }
253
+ const httpBaseUrl = this.config.baseUrl.replace("wss://", "https://").replace("ws://", "http://");
254
+ const isDelegate = this.config.apiKey.startsWith("del_") && this.config.apiKey.includes(":");
255
+ const authHeader = isDelegate ? `ApiKey ${this.config.apiKey}` : `Bearer ${this.config.apiKey}`;
256
+ const response = await fetch(`${httpBaseUrl}/api/ws-token`, {
257
+ method: "POST",
258
+ headers: {
259
+ "Content-Type": "application/json",
260
+ "Authorization": authHeader
261
+ },
262
+ body: JSON.stringify({
263
+ instanceId: this.config.instanceId,
264
+ permission: "write"
265
+ })
266
+ });
267
+ if (!response.ok) {
268
+ const error = await response.json().catch(() => ({ error: "Failed to get token" }));
269
+ throw new Error(error.error || `Failed to get WebSocket token: ${response.status}`);
270
+ }
271
+ const data = await response.json();
272
+ return data.token;
273
+ }
88
274
  /**
89
275
  * Connect to the cloud service
90
276
  */
@@ -94,13 +280,19 @@ var init_CloudAdapter = __esm({
94
280
  }
95
281
  this._state = "connecting";
96
282
  try {
97
- if (this.config.tokenProvider && !this.token) {
98
- this.token = await this.config.tokenProvider();
283
+ if (!this.token) {
284
+ if (this.config.tokenProvider) {
285
+ this.token = await this.config.tokenProvider();
286
+ } else if (this.config.apiKey) {
287
+ this.token = await this.fetchTokenWithApiKey();
288
+ }
99
289
  }
100
290
  let url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
101
291
  if (this.token) {
102
292
  url += `?token=${encodeURIComponent(this.token)}`;
103
293
  this.token = null;
294
+ } else {
295
+ throw new Error("MindCache Cloud: No authentication method available. Provide apiKey or tokenProvider.");
104
296
  }
105
297
  this.ws = new WebSocket(url);
106
298
  this.setupWebSocket();
@@ -162,12 +354,6 @@ var init_CloudAdapter = __esm({
162
354
  return;
163
355
  }
164
356
  this.ws.onopen = () => {
165
- if (this.config.apiKey) {
166
- this.ws.send(JSON.stringify({
167
- type: "auth",
168
- apiKey: this.config.apiKey
169
- }));
170
- }
171
357
  };
172
358
  this.ws.onmessage = (event) => {
173
359
  try {
@@ -175,6 +361,7 @@ var init_CloudAdapter = __esm({
175
361
  this.handleMessage(msg);
176
362
  } catch (error) {
177
363
  console.error("MindCache Cloud: Failed to parse message:", error);
364
+ console.error("Raw message:", typeof event.data === "string" ? event.data.slice(0, 200) : event.data);
178
365
  }
179
366
  };
180
367
  this.ws.onclose = () => {
@@ -182,10 +369,12 @@ var init_CloudAdapter = __esm({
182
369
  this.emit("disconnected");
183
370
  this.scheduleReconnect();
184
371
  };
185
- this.ws.onerror = (error) => {
372
+ this.ws.onerror = () => {
186
373
  this._state = "error";
187
- this.emit("error", new Error("WebSocket error"));
188
- console.error("MindCache Cloud: WebSocket error:", error);
374
+ const url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
375
+ console.error(`MindCache Cloud: WebSocket error connecting to ${url}`);
376
+ console.error("Check that the instance ID and API key are correct, and that the server is reachable.");
377
+ this.emit("error", new Error(`WebSocket connection failed to ${url}`));
189
378
  };
190
379
  }
191
380
  handleMessage(msg) {
@@ -262,15 +451,6 @@ var init_CloudAdapter = __esm({
262
451
  this.reconnectTimeout = setTimeout(async () => {
263
452
  this.reconnectTimeout = null;
264
453
  this.reconnectAttempts++;
265
- if (this.config.tokenProvider) {
266
- try {
267
- this.token = await this.config.tokenProvider();
268
- } catch (error) {
269
- console.error("MindCache Cloud: Failed to get token for reconnect:", error);
270
- this.emit("error", error);
271
- return;
272
- }
273
- }
274
454
  this.connect();
275
455
  }, delay);
276
456
  }
@@ -299,8 +479,8 @@ var init_CloudAdapter = __esm({
299
479
  var DEFAULT_KEY_ATTRIBUTES = {
300
480
  type: "text",
301
481
  contentTags: [],
302
- systemTags: ["prompt"],
303
- // visible by default
482
+ systemTags: ["SystemPrompt", "LLMWrite"],
483
+ // visible in system prompt and writable by LLM by default
304
484
  zIndex: 0,
305
485
  // Legacy - derived from systemTags
306
486
  readonly: false,
@@ -317,6 +497,65 @@ var MindCache = class {
317
497
  globalListeners = [];
318
498
  // Internal flag to prevent sync loops when receiving remote updates
319
499
  _isRemoteUpdate = false;
500
+ /**
501
+ * Normalize system tags: migrate old tags to new ones
502
+ * - 'prompt' → 'SystemPrompt'
503
+ * - 'readonly' → remove 'LLMWrite' (or add if not readonly)
504
+ */
505
+ normalizeSystemTags(tags) {
506
+ const normalized = [];
507
+ let hasSystemPrompt = false;
508
+ let hasLLMRead = false;
509
+ let hasLLMWrite = false;
510
+ let hasReadonly = false;
511
+ for (const tag of tags) {
512
+ if (tag === "SystemPrompt" || tag === "prompt") {
513
+ hasSystemPrompt = true;
514
+ } else if (tag === "LLMRead") {
515
+ hasLLMRead = true;
516
+ } else if (tag === "LLMWrite") {
517
+ hasLLMWrite = true;
518
+ } else if (tag === "readonly") {
519
+ hasReadonly = true;
520
+ } else if (tag === "protected") {
521
+ normalized.push(tag);
522
+ } else if (tag === "ApplyTemplate" || tag === "template") {
523
+ normalized.push("ApplyTemplate");
524
+ }
525
+ }
526
+ if (hasSystemPrompt) {
527
+ normalized.push("SystemPrompt");
528
+ }
529
+ if (hasLLMRead) {
530
+ normalized.push("LLMRead");
531
+ }
532
+ if (hasReadonly) {
533
+ normalized.push("readonly");
534
+ } else if (hasLLMWrite) {
535
+ normalized.push("LLMWrite");
536
+ } else {
537
+ normalized.push("LLMWrite");
538
+ }
539
+ return normalized;
540
+ }
541
+ /**
542
+ * Check if key should be visible in system prompt
543
+ */
544
+ hasSystemPrompt(tags) {
545
+ return tags.includes("SystemPrompt") || tags.includes("prompt");
546
+ }
547
+ /**
548
+ * Check if key can be read by LLM (has LLMRead or SystemPrompt)
549
+ */
550
+ hasLLMRead(tags) {
551
+ return tags.includes("LLMRead") || tags.includes("SystemPrompt") || tags.includes("prompt");
552
+ }
553
+ /**
554
+ * Check if key can be written by LLM (has LLMWrite and not readonly)
555
+ */
556
+ hasLLMWrite(tags) {
557
+ return tags.includes("LLMWrite") && !tags.includes("readonly");
558
+ }
320
559
  // Cloud sync state
321
560
  _cloudAdapter = null;
322
561
  _connectionState = "disconnected";
@@ -325,15 +564,33 @@ var MindCache = class {
325
564
  _cloudConfig = null;
326
565
  // Access level for system operations
327
566
  _accessLevel = "user";
567
+ _initPromise = null;
328
568
  constructor(options) {
329
569
  if (options?.accessLevel) {
330
570
  this._accessLevel = options.accessLevel;
331
571
  }
572
+ if (options?.cloud && options?.indexedDB) {
573
+ throw new Error(
574
+ "MindCache: Cannot use both cloud and indexedDB together. Choose one persistence method to avoid data conflicts. Use cloud for real-time sync, or indexedDB for local-only persistence."
575
+ );
576
+ }
577
+ const initPromises = [];
332
578
  if (options?.cloud) {
333
579
  this._cloudConfig = options.cloud;
334
580
  this._isLoaded = false;
335
581
  this._connectionState = "disconnected";
336
- this._initCloud();
582
+ initPromises.push(this._initCloud());
583
+ }
584
+ if (options?.indexedDB) {
585
+ this._isLoaded = false;
586
+ initPromises.push(this._initIndexedDB(options.indexedDB));
587
+ }
588
+ if (initPromises.length > 0) {
589
+ this._initPromise = Promise.all(initPromises).then(() => {
590
+ if (!this._cloudConfig) {
591
+ this._isLoaded = true;
592
+ }
593
+ });
337
594
  }
338
595
  }
339
596
  /**
@@ -353,9 +610,11 @@ var MindCache = class {
353
610
  return;
354
611
  }
355
612
  try {
356
- const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
357
- const configUrl = this._cloudConfig.baseUrl || "https://api.mindcache.io";
358
- const baseUrl = configUrl.replace("https://", "wss://").replace("http://", "ws://");
613
+ const CloudAdapter2 = await this._getCloudAdapterClass();
614
+ if (!this._cloudConfig.baseUrl) {
615
+ throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
616
+ }
617
+ const baseUrl = this._cloudConfig.baseUrl.replace("https://", "wss://").replace("http://", "ws://");
359
618
  const adapter = new CloudAdapter2({
360
619
  instanceId: this._cloudConfig.instanceId,
361
620
  projectId: this._cloudConfig.projectId || "default",
@@ -411,6 +670,19 @@ var MindCache = class {
411
670
  this._isLoaded = true;
412
671
  }
413
672
  }
673
+ async _initIndexedDB(config) {
674
+ try {
675
+ const IndexedDBAdapter2 = await this._getIndexedDBAdapterClass();
676
+ const adapter = new IndexedDBAdapter2(config);
677
+ await adapter.attach(this);
678
+ } catch (error) {
679
+ console.error("MindCache: Failed to initialize IndexedDB:", error);
680
+ }
681
+ }
682
+ async _getIndexedDBAdapterClass() {
683
+ const { IndexedDBAdapter: IndexedDBAdapter2 } = await Promise.resolve().then(() => (init_IndexedDBAdapter(), IndexedDBAdapter_exports));
684
+ return IndexedDBAdapter2;
685
+ }
414
686
  /**
415
687
  * Get the current cloud connection state
416
688
  */
@@ -423,12 +695,46 @@ var MindCache = class {
423
695
  get isLoaded() {
424
696
  return this._isLoaded;
425
697
  }
698
+ /**
699
+ * Protected method to load CloudAdapter class.
700
+ * Can be overridden/mocked for testing.
701
+ */
702
+ async _getCloudAdapterClass() {
703
+ const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
704
+ return CloudAdapter2;
705
+ }
426
706
  /**
427
707
  * Check if this instance is connected to cloud
428
708
  */
429
709
  get isCloud() {
430
710
  return this._cloudConfig !== null;
431
711
  }
712
+ /**
713
+ * Wait for initial sync to complete (or resolve immediately if already synced/local).
714
+ * Useful for scripts or linear execution flows.
715
+ */
716
+ async waitForSync() {
717
+ if (this._isLoaded) {
718
+ return;
719
+ }
720
+ if (this._initPromise) {
721
+ await this._initPromise;
722
+ }
723
+ if (this._isLoaded) {
724
+ return;
725
+ }
726
+ return new Promise((resolve) => {
727
+ if (!this._cloudAdapter) {
728
+ resolve();
729
+ return;
730
+ }
731
+ const handler = () => {
732
+ this._cloudAdapter?.off("synced", handler);
733
+ resolve();
734
+ };
735
+ this._cloudAdapter.on("synced", handler);
736
+ });
737
+ }
432
738
  /**
433
739
  * Disconnect from cloud (if connected)
434
740
  */
@@ -495,7 +801,7 @@ var MindCache = class {
495
801
  if (!entry) {
496
802
  return void 0;
497
803
  }
498
- if (entry.attributes.template) {
804
+ if (entry.attributes.systemTags?.includes("ApplyTemplate") || entry.attributes.systemTags?.includes("template") || entry.attributes.template) {
499
805
  const processingStack = _processingStack || /* @__PURE__ */ new Set();
500
806
  if (processingStack.has(key)) {
501
807
  return entry.value;
@@ -544,77 +850,87 @@ var MindCache = class {
544
850
  ...DEFAULT_KEY_ATTRIBUTES,
545
851
  contentTags: [],
546
852
  // Fresh array
547
- systemTags: ["prompt"],
853
+ systemTags: ["SystemPrompt", "LLMWrite"],
548
854
  // Fresh array with default
549
855
  tags: [],
550
856
  // Fresh array
551
857
  zIndex: 0
552
858
  };
553
859
  const finalAttributes = attributes ? { ...baseAttributes, ...attributes } : baseAttributes;
860
+ let systemTags = this.normalizeSystemTags(finalAttributes.systemTags || []);
554
861
  if (attributes) {
555
- let systemTags2 = [...finalAttributes.systemTags || []];
556
862
  if ("readonly" in attributes) {
557
- if (attributes.readonly && !systemTags2.includes("readonly")) {
558
- systemTags2.push("readonly");
559
- } else if (!attributes.readonly && !wasHardcoded) {
560
- systemTags2 = systemTags2.filter((t) => t !== "readonly");
863
+ if (attributes.readonly) {
864
+ systemTags = systemTags.filter((t) => t !== "LLMWrite");
865
+ if (!systemTags.includes("readonly")) {
866
+ systemTags.push("readonly");
867
+ }
868
+ } else if (!wasHardcoded) {
869
+ if (!systemTags.includes("LLMWrite")) {
870
+ systemTags.push("LLMWrite");
871
+ }
872
+ systemTags = systemTags.filter((t) => t !== "readonly");
561
873
  }
562
874
  }
563
875
  if ("visible" in attributes) {
564
- if (attributes.visible && !systemTags2.includes("prompt")) {
565
- systemTags2.push("prompt");
566
- } else if (!attributes.visible) {
567
- systemTags2 = systemTags2.filter((t) => t !== "prompt");
876
+ if (attributes.visible) {
877
+ if (!systemTags.includes("SystemPrompt")) {
878
+ systemTags.push("SystemPrompt");
879
+ }
880
+ systemTags = systemTags.filter((t) => t !== "prompt");
881
+ } else {
882
+ systemTags = systemTags.filter((t) => t !== "SystemPrompt" && t !== "prompt");
568
883
  }
569
884
  }
885
+ if ("systemTags" in attributes && Array.isArray(attributes.systemTags)) {
886
+ systemTags = this.normalizeSystemTags(attributes.systemTags);
887
+ }
570
888
  if ("hardcoded" in attributes) {
571
- if (attributes.hardcoded && !systemTags2.includes("protected")) {
572
- systemTags2.push("protected");
889
+ if (attributes.hardcoded && !systemTags.includes("protected")) {
890
+ systemTags.push("protected");
573
891
  } else if (!attributes.hardcoded && !wasHardcoded) {
574
- systemTags2 = systemTags2.filter((t) => t !== "protected");
892
+ systemTags = systemTags.filter((t) => t !== "protected");
575
893
  }
576
- if (wasHardcoded && !systemTags2.includes("protected")) {
577
- systemTags2.push("protected");
894
+ if (wasHardcoded && !systemTags.includes("protected")) {
895
+ systemTags.push("protected");
578
896
  }
579
897
  } else if (wasHardcoded) {
580
- if (!systemTags2.includes("protected")) {
581
- systemTags2.push("protected");
898
+ if (!systemTags.includes("protected")) {
899
+ systemTags.push("protected");
582
900
  }
583
901
  }
584
902
  if ("template" in attributes) {
585
- if (attributes.template && !wasHardcoded && !systemTags2.includes("template")) {
586
- systemTags2.push("template");
903
+ if (attributes.template && !wasHardcoded && !systemTags.includes("ApplyTemplate") && !systemTags.includes("template")) {
904
+ systemTags.push("ApplyTemplate");
587
905
  } else if (!attributes.template || wasHardcoded) {
588
- systemTags2 = systemTags2.filter((t) => t !== "template");
906
+ systemTags = systemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
589
907
  }
590
908
  }
591
- finalAttributes.systemTags = systemTags2;
592
909
  } else if (wasHardcoded) {
593
- let systemTags2 = [...finalAttributes.systemTags || []];
594
- if (!systemTags2.includes("protected")) {
595
- systemTags2.push("protected");
910
+ if (!systemTags.includes("protected")) {
911
+ systemTags.push("protected");
596
912
  }
597
- if (!systemTags2.includes("readonly")) {
598
- systemTags2.push("readonly");
913
+ systemTags = systemTags.filter((t) => t !== "LLMWrite");
914
+ if (!systemTags.includes("readonly")) {
915
+ systemTags.push("readonly");
599
916
  }
600
- systemTags2 = systemTags2.filter((t) => t !== "template");
601
- finalAttributes.systemTags = systemTags2;
917
+ systemTags = systemTags.filter((t) => t !== "template");
602
918
  }
603
- let systemTags = finalAttributes.systemTags || [];
604
919
  if (wasHardcoded && !systemTags.includes("protected")) {
605
- systemTags = [...systemTags, "protected"];
920
+ systemTags.push("protected");
606
921
  }
607
922
  if (systemTags.includes("protected")) {
923
+ systemTags = systemTags.filter((t) => t !== "LLMWrite");
608
924
  if (!systemTags.includes("readonly")) {
609
- systemTags = [...systemTags, "readonly"];
925
+ systemTags.push("readonly");
610
926
  }
611
927
  systemTags = systemTags.filter((t) => t !== "template");
612
- finalAttributes.systemTags = systemTags;
613
928
  }
614
- finalAttributes.readonly = systemTags.includes("readonly");
615
- finalAttributes.visible = systemTags.includes("prompt");
929
+ finalAttributes.systemTags = systemTags;
930
+ finalAttributes.readonly = systemTags.includes("readonly") || !systemTags.includes("LLMWrite");
931
+ finalAttributes.visible = this.hasSystemPrompt(systemTags);
616
932
  finalAttributes.hardcoded = wasHardcoded || systemTags.includes("protected");
617
- finalAttributes.template = systemTags.includes("template");
933
+ finalAttributes.template = systemTags.includes("ApplyTemplate") || systemTags.includes("template");
618
934
  if (attributes && "tags" in attributes && attributes.tags) {
619
935
  finalAttributes.contentTags = [...attributes.tags];
620
936
  }
@@ -635,21 +951,25 @@ var MindCache = class {
635
951
  return;
636
952
  }
637
953
  this._isRemoteUpdate = true;
638
- const systemTags = attributes.systemTags || [];
639
- if (!attributes.systemTags) {
954
+ let systemTags = attributes.systemTags || [];
955
+ if (!attributes.systemTags || systemTags.length === 0) {
956
+ systemTags = [];
640
957
  if (attributes.visible !== false) {
641
958
  systemTags.push("prompt");
642
959
  }
643
960
  if (attributes.readonly) {
644
961
  systemTags.push("readonly");
962
+ } else {
963
+ systemTags.push("LLMWrite");
645
964
  }
646
965
  if (attributes.hardcoded) {
647
966
  systemTags.push("protected");
648
967
  }
649
968
  if (attributes.template) {
650
- systemTags.push("template");
969
+ systemTags.push("ApplyTemplate");
651
970
  }
652
971
  }
972
+ systemTags = this.normalizeSystemTags(systemTags);
653
973
  const contentTags = attributes.contentTags || attributes.tags || [];
654
974
  this.stm[key] = {
655
975
  value,
@@ -659,10 +979,11 @@ var MindCache = class {
659
979
  systemTags,
660
980
  zIndex: attributes.zIndex ?? 0,
661
981
  tags: contentTags,
662
- readonly: systemTags.includes("readonly"),
663
- visible: systemTags.includes("prompt"),
982
+ // Sync legacy attributes FROM normalized systemTags
983
+ readonly: systemTags.includes("readonly") || !systemTags.includes("LLMWrite"),
984
+ visible: this.hasSystemPrompt(systemTags),
664
985
  hardcoded: systemTags.includes("protected"),
665
- template: systemTags.includes("template")
986
+ template: systemTags.includes("ApplyTemplate") || systemTags.includes("template")
666
987
  }
667
988
  };
668
989
  if (this.listeners[key]) {
@@ -717,39 +1038,55 @@ var MindCache = class {
717
1038
  }
718
1039
  }
719
1040
  entry.attributes = { ...entry.attributes, ...allowedAttributes };
720
- if ("readonly" in attributes || "visible" in attributes || "template" in attributes) {
721
- let newSystemTags = [];
722
- if (entry.attributes.readonly) {
723
- newSystemTags.push("readonly");
724
- }
725
- if (entry.attributes.visible) {
726
- newSystemTags.push("prompt");
727
- }
728
- if (entry.attributes.template) {
729
- newSystemTags.push("template");
730
- }
731
- if (wasHardcoded || entry.attributes.hardcoded) {
732
- newSystemTags.push("protected");
1041
+ if ("readonly" in attributes || "visible" in attributes || "template" in attributes || "systemTags" in attributes) {
1042
+ let newSystemTags = entry.attributes.systemTags || [];
1043
+ if ("systemTags" in attributes && Array.isArray(attributes.systemTags)) {
1044
+ newSystemTags = this.normalizeSystemTags(attributes.systemTags);
1045
+ } else {
1046
+ newSystemTags = [];
1047
+ if (!entry.attributes.readonly) {
1048
+ newSystemTags.push("LLMWrite");
1049
+ } else {
1050
+ newSystemTags.push("readonly");
1051
+ }
1052
+ if (entry.attributes.visible) {
1053
+ newSystemTags.push("SystemPrompt");
1054
+ }
1055
+ if (entry.attributes.template) {
1056
+ newSystemTags.push("ApplyTemplate");
1057
+ }
1058
+ if (wasHardcoded || entry.attributes.hardcoded) {
1059
+ newSystemTags.push("protected");
1060
+ }
1061
+ newSystemTags = this.normalizeSystemTags(newSystemTags);
733
1062
  }
734
1063
  if (newSystemTags.includes("protected")) {
1064
+ newSystemTags = newSystemTags.filter((t) => t !== "LLMWrite");
735
1065
  if (!newSystemTags.includes("readonly")) {
736
1066
  newSystemTags.push("readonly");
737
1067
  }
738
- newSystemTags = newSystemTags.filter((t) => t !== "template");
1068
+ newSystemTags = newSystemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
739
1069
  entry.attributes.readonly = true;
740
1070
  entry.attributes.template = false;
741
1071
  }
742
1072
  entry.attributes.systemTags = newSystemTags;
1073
+ entry.attributes.readonly = newSystemTags.includes("readonly") || !newSystemTags.includes("LLMWrite");
1074
+ entry.attributes.visible = this.hasSystemPrompt(newSystemTags);
1075
+ entry.attributes.template = newSystemTags.includes("ApplyTemplate") || newSystemTags.includes("template");
743
1076
  } else if (wasHardcoded) {
744
- let systemTags = [...entry.attributes.systemTags || []];
1077
+ let systemTags = this.normalizeSystemTags(entry.attributes.systemTags || []);
745
1078
  if (!systemTags.includes("protected")) {
746
1079
  systemTags.push("protected");
747
1080
  }
1081
+ systemTags = systemTags.filter((t) => t !== "LLMWrite");
748
1082
  if (!systemTags.includes("readonly")) {
749
1083
  systemTags.push("readonly");
750
1084
  }
751
- systemTags = systemTags.filter((t) => t !== "template");
1085
+ systemTags = systemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
752
1086
  entry.attributes.systemTags = systemTags;
1087
+ entry.attributes.readonly = true;
1088
+ entry.attributes.visible = this.hasSystemPrompt(systemTags);
1089
+ entry.attributes.template = false;
753
1090
  }
754
1091
  if (wasHardcoded) {
755
1092
  entry.attributes.hardcoded = true;
@@ -991,8 +1328,9 @@ var MindCache = class {
991
1328
  const sortedKeys = this.getSortedKeys();
992
1329
  sortedKeys.forEach((key) => {
993
1330
  const entry = this.stm[key];
994
- if (entry.attributes.visible) {
995
- const processedValue = entry.attributes.template ? this.get_value(key) : entry.value;
1331
+ if (this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) {
1332
+ const hasTemplate = entry.attributes.systemTags?.includes("ApplyTemplate") || entry.attributes.systemTags?.includes("template") || entry.attributes.template;
1333
+ const processedValue = hasTemplate ? this.get_value(key) : entry.value;
996
1334
  apiData.push({
997
1335
  key,
998
1336
  value: processedValue,
@@ -1018,7 +1356,7 @@ var MindCache = class {
1018
1356
  const sortedKeys = this.getSortedKeys();
1019
1357
  sortedKeys.forEach((key) => {
1020
1358
  const entry = this.stm[key];
1021
- if (entry.attributes.visible && entry.attributes.type === "image" && entry.attributes.contentType) {
1359
+ if ((this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) && entry.attributes.type === "image" && entry.attributes.contentType) {
1022
1360
  const dataUrl = this.createDataUrl(entry.value, entry.attributes.contentType);
1023
1361
  imageParts.push({
1024
1362
  type: "file",
@@ -1057,6 +1395,7 @@ var MindCache = class {
1057
1395
  }
1058
1396
  deserialize(data) {
1059
1397
  if (typeof data === "object" && data !== null) {
1398
+ this._isRemoteUpdate = true;
1060
1399
  this.clear();
1061
1400
  Object.entries(data).forEach(([key, entry]) => {
1062
1401
  if (entry && typeof entry === "object" && "value" in entry && "attributes" in entry) {
@@ -1065,21 +1404,24 @@ var MindCache = class {
1065
1404
  return;
1066
1405
  }
1067
1406
  let systemTags = attrs.systemTags || [];
1068
- if (!attrs.systemTags) {
1407
+ if (!attrs.systemTags || systemTags.length === 0) {
1069
1408
  systemTags = [];
1070
1409
  if (attrs.visible !== false) {
1071
1410
  systemTags.push("prompt");
1072
1411
  }
1073
1412
  if (attrs.readonly) {
1074
1413
  systemTags.push("readonly");
1414
+ } else {
1415
+ systemTags.push("LLMWrite");
1075
1416
  }
1076
1417
  if (attrs.hardcoded) {
1077
1418
  systemTags.push("protected");
1078
1419
  }
1079
1420
  if (attrs.template) {
1080
- systemTags.push("template");
1421
+ systemTags.push("ApplyTemplate");
1081
1422
  }
1082
1423
  }
1424
+ systemTags = this.normalizeSystemTags(systemTags);
1083
1425
  const contentTags = attrs.contentTags || attrs.tags || [];
1084
1426
  this.stm[key] = {
1085
1427
  value: entry.value,
@@ -1088,17 +1430,18 @@ var MindCache = class {
1088
1430
  contentTags,
1089
1431
  systemTags,
1090
1432
  zIndex: attrs.zIndex ?? 0,
1091
- // Sync legacy attributes
1433
+ // Sync legacy attributes FROM normalized systemTags
1092
1434
  tags: contentTags,
1093
- readonly: systemTags.includes("readonly"),
1094
- visible: systemTags.includes("prompt"),
1435
+ readonly: systemTags.includes("readonly") || !systemTags.includes("LLMWrite"),
1436
+ visible: this.hasSystemPrompt(systemTags),
1095
1437
  hardcoded: systemTags.includes("protected"),
1096
- template: systemTags.includes("template")
1438
+ template: systemTags.includes("ApplyTemplate") || systemTags.includes("template")
1097
1439
  }
1098
1440
  };
1099
1441
  }
1100
1442
  });
1101
1443
  this.notifyGlobalListeners();
1444
+ this._isRemoteUpdate = false;
1102
1445
  }
1103
1446
  }
1104
1447
  get_system_prompt() {
@@ -1107,13 +1450,14 @@ var MindCache = class {
1107
1450
  const sortedKeys = this.getSortedKeys();
1108
1451
  sortedKeys.forEach((key) => {
1109
1452
  const entry = this.stm[key];
1110
- if (entry.attributes.visible) {
1453
+ if (this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) {
1111
1454
  if (entry.attributes.type === "image") {
1112
1455
  promptLines.push(`image ${key} available`);
1113
1456
  return;
1114
1457
  }
1115
1458
  if (entry.attributes.type === "file") {
1116
- if (entry.attributes.readonly) {
1459
+ const canWrite2 = this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
1460
+ if (!canWrite2) {
1117
1461
  promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}`);
1118
1462
  } else {
1119
1463
  const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -1123,7 +1467,8 @@ var MindCache = class {
1123
1467
  }
1124
1468
  const value = this.get_value(key);
1125
1469
  const formattedValue = typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
1126
- if (entry.attributes.readonly) {
1470
+ const canWrite = this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
1471
+ if (!canWrite) {
1127
1472
  promptLines.push(`${key}: ${formattedValue}`);
1128
1473
  } else {
1129
1474
  const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -1151,7 +1496,7 @@ var MindCache = class {
1151
1496
  const sortedKeys = this.getSortedKeys();
1152
1497
  const writableKeys = sortedKeys.filter((key) => {
1153
1498
  const entry = this.stm[key];
1154
- return !entry.attributes.readonly;
1499
+ return this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
1155
1500
  });
1156
1501
  writableKeys.forEach((key) => {
1157
1502
  const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -1226,7 +1571,8 @@ var MindCache = class {
1226
1571
  return null;
1227
1572
  }
1228
1573
  const entry = this.stm[originalKey];
1229
- if (entry && entry.attributes.readonly) {
1574
+ const canWrite = entry && (this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly"));
1575
+ if (!canWrite) {
1230
1576
  return null;
1231
1577
  }
1232
1578
  this.set_value(originalKey, value);
@@ -1473,7 +1819,7 @@ var MindCache = class {
1473
1819
  entry.attributes.readonly = tags.includes("readonly");
1474
1820
  entry.attributes.visible = tags.includes("prompt");
1475
1821
  entry.attributes.hardcoded = tags.includes("protected");
1476
- entry.attributes.template = tags.includes("template");
1822
+ entry.attributes.template = tags.includes("ApplyTemplate") || tags.includes("template");
1477
1823
  }
1478
1824
  toMarkdown() {
1479
1825
  const now = /* @__PURE__ */ new Date();
@@ -1714,6 +2060,8 @@ var MindCache = class {
1714
2060
  }
1715
2061
  if (attrs.readonly) {
1716
2062
  systemTags.push("readonly");
2063
+ } else {
2064
+ systemTags.push("LLMWrite");
1717
2065
  }
1718
2066
  if (attrs.hardcoded) {
1719
2067
  systemTags.push("protected");
@@ -1721,7 +2069,9 @@ var MindCache = class {
1721
2069
  if (attrs.template) {
1722
2070
  systemTags.push("template");
1723
2071
  }
1724
- attrs.systemTags = systemTags;
2072
+ attrs.systemTags = this.normalizeSystemTags(systemTags);
2073
+ } else {
2074
+ attrs.systemTags = this.normalizeSystemTags(attrs.systemTags);
1725
2075
  }
1726
2076
  if (!attrs.contentTags) {
1727
2077
  attrs.contentTags = [];
@@ -1729,6 +2079,11 @@ var MindCache = class {
1729
2079
  if (!attrs.tags) {
1730
2080
  attrs.tags = [...attrs.contentTags];
1731
2081
  }
2082
+ const normalizedTags = attrs.systemTags || [];
2083
+ attrs.readonly = normalizedTags.includes("readonly") || !normalizedTags.includes("LLMWrite");
2084
+ attrs.visible = this.hasSystemPrompt(normalizedTags);
2085
+ attrs.hardcoded = normalizedTags.includes("protected");
2086
+ attrs.template = normalizedTags.includes("ApplyTemplate") || normalizedTags.includes("template");
1732
2087
  this.stm[key] = {
1733
2088
  value: entry.value,
1734
2089
  attributes: attrs