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