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