mindcache 2.0.0 → 2.3.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
@@ -17,16 +17,17 @@ var CloudAdapter_exports = {};
17
17
  __export(CloudAdapter_exports, {
18
18
  CloudAdapter: () => exports.CloudAdapter
19
19
  });
20
- var DEFAULT_BASE_URL, RECONNECT_DELAY, MAX_RECONNECT_DELAY; exports.CloudAdapter = void 0;
20
+ var RECONNECT_DELAY, MAX_RECONNECT_DELAY; exports.CloudAdapter = void 0;
21
21
  var init_CloudAdapter = __esm({
22
22
  "src/cloud/CloudAdapter.ts"() {
23
- DEFAULT_BASE_URL = "wss://api.mindcache.io";
24
23
  RECONNECT_DELAY = 1e3;
25
24
  MAX_RECONNECT_DELAY = 3e4;
26
25
  exports.CloudAdapter = class {
27
26
  constructor(config) {
28
27
  this.config = config;
29
- this.config.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
28
+ if (!config.baseUrl) {
29
+ throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
30
+ }
30
31
  }
31
32
  ws = null;
32
33
  queue = [];
@@ -87,6 +88,40 @@ var init_CloudAdapter = __esm({
87
88
  }
88
89
  this.mindcache = null;
89
90
  }
91
+ /**
92
+ * Fetch a short-lived WebSocket token from the API using the API key.
93
+ * This keeps the API key secure by only using it for a single HTTPS request,
94
+ * then using the short-lived token for the WebSocket connection.
95
+ *
96
+ * Supports two key formats:
97
+ * - API keys: mc_live_xxx or mc_test_xxx → Bearer token
98
+ * - Delegate keys: del_xxx:sec_xxx → ApiKey format
99
+ */
100
+ async fetchTokenWithApiKey() {
101
+ if (!this.config.apiKey) {
102
+ throw new Error("API key is required to fetch token");
103
+ }
104
+ const httpBaseUrl = this.config.baseUrl.replace("wss://", "https://").replace("ws://", "http://");
105
+ const isDelegate = this.config.apiKey.startsWith("del_") && this.config.apiKey.includes(":");
106
+ const authHeader = isDelegate ? `ApiKey ${this.config.apiKey}` : `Bearer ${this.config.apiKey}`;
107
+ const response = await fetch(`${httpBaseUrl}/api/ws-token`, {
108
+ method: "POST",
109
+ headers: {
110
+ "Content-Type": "application/json",
111
+ "Authorization": authHeader
112
+ },
113
+ body: JSON.stringify({
114
+ instanceId: this.config.instanceId,
115
+ permission: "write"
116
+ })
117
+ });
118
+ if (!response.ok) {
119
+ const error = await response.json().catch(() => ({ error: "Failed to get token" }));
120
+ throw new Error(error.error || `Failed to get WebSocket token: ${response.status}`);
121
+ }
122
+ const data = await response.json();
123
+ return data.token;
124
+ }
90
125
  /**
91
126
  * Connect to the cloud service
92
127
  */
@@ -96,13 +131,19 @@ var init_CloudAdapter = __esm({
96
131
  }
97
132
  this._state = "connecting";
98
133
  try {
99
- if (this.config.tokenProvider && !this.token) {
100
- this.token = await this.config.tokenProvider();
134
+ if (!this.token) {
135
+ if (this.config.tokenProvider) {
136
+ this.token = await this.config.tokenProvider();
137
+ } else if (this.config.apiKey) {
138
+ this.token = await this.fetchTokenWithApiKey();
139
+ }
101
140
  }
102
141
  let url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
103
142
  if (this.token) {
104
143
  url += `?token=${encodeURIComponent(this.token)}`;
105
144
  this.token = null;
145
+ } else {
146
+ throw new Error("MindCache Cloud: No authentication method available. Provide apiKey or tokenProvider.");
106
147
  }
107
148
  this.ws = new WebSocket(url);
108
149
  this.setupWebSocket();
@@ -164,12 +205,6 @@ var init_CloudAdapter = __esm({
164
205
  return;
165
206
  }
166
207
  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
208
  };
174
209
  this.ws.onmessage = (event) => {
175
210
  try {
@@ -177,6 +212,7 @@ var init_CloudAdapter = __esm({
177
212
  this.handleMessage(msg);
178
213
  } catch (error) {
179
214
  console.error("MindCache Cloud: Failed to parse message:", error);
215
+ console.error("Raw message:", typeof event.data === "string" ? event.data.slice(0, 200) : event.data);
180
216
  }
181
217
  };
182
218
  this.ws.onclose = () => {
@@ -184,10 +220,12 @@ var init_CloudAdapter = __esm({
184
220
  this.emit("disconnected");
185
221
  this.scheduleReconnect();
186
222
  };
187
- this.ws.onerror = (error) => {
223
+ this.ws.onerror = () => {
188
224
  this._state = "error";
189
- this.emit("error", new Error("WebSocket error"));
190
- console.error("MindCache Cloud: WebSocket error:", error);
225
+ const url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
226
+ console.error(`MindCache Cloud: WebSocket error connecting to ${url}`);
227
+ console.error("Check that the instance ID and API key are correct, and that the server is reachable.");
228
+ this.emit("error", new Error(`WebSocket connection failed to ${url}`));
191
229
  };
192
230
  }
193
231
  handleMessage(msg) {
@@ -264,15 +302,6 @@ var init_CloudAdapter = __esm({
264
302
  this.reconnectTimeout = setTimeout(async () => {
265
303
  this.reconnectTimeout = null;
266
304
  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
305
  this.connect();
277
306
  }, delay);
278
307
  }
@@ -299,11 +328,16 @@ var init_CloudAdapter = __esm({
299
328
 
300
329
  // src/core/types.ts
301
330
  var DEFAULT_KEY_ATTRIBUTES = {
331
+ type: "text",
332
+ contentTags: [],
333
+ systemTags: ["prompt"],
334
+ // visible by default
335
+ zIndex: 0,
336
+ // Legacy - derived from systemTags
302
337
  readonly: false,
303
338
  visible: true,
304
339
  hardcoded: false,
305
340
  template: false,
306
- type: "text",
307
341
  tags: []
308
342
  };
309
343
 
@@ -320,22 +354,42 @@ var MindCache = class {
320
354
  _isLoaded = true;
321
355
  // Default true for local mode
322
356
  _cloudConfig = null;
357
+ // Access level for system operations
358
+ _accessLevel = "user";
359
+ _initPromise = null;
323
360
  constructor(options) {
361
+ if (options?.accessLevel) {
362
+ this._accessLevel = options.accessLevel;
363
+ }
324
364
  if (options?.cloud) {
325
365
  this._cloudConfig = options.cloud;
326
366
  this._isLoaded = false;
327
367
  this._connectionState = "disconnected";
328
- this._initCloud();
368
+ this._initPromise = this._initCloud();
329
369
  }
330
370
  }
371
+ /**
372
+ * Get the current access level
373
+ */
374
+ get accessLevel() {
375
+ return this._accessLevel;
376
+ }
377
+ /**
378
+ * Check if this instance has system-level access
379
+ */
380
+ get hasSystemAccess() {
381
+ return this._accessLevel === "system";
382
+ }
331
383
  async _initCloud() {
332
384
  if (!this._cloudConfig) {
333
385
  return;
334
386
  }
335
387
  try {
336
- const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
337
- const configUrl = this._cloudConfig.baseUrl || "https://api.mindcache.io";
338
- const baseUrl = configUrl.replace("https://", "wss://").replace("http://", "ws://");
388
+ const CloudAdapter2 = await this._getCloudAdapterClass();
389
+ if (!this._cloudConfig.baseUrl) {
390
+ throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
391
+ }
392
+ const baseUrl = this._cloudConfig.baseUrl.replace("https://", "wss://").replace("http://", "ws://");
339
393
  const adapter = new CloudAdapter2({
340
394
  instanceId: this._cloudConfig.instanceId,
341
395
  projectId: this._cloudConfig.projectId || "default",
@@ -403,12 +457,46 @@ var MindCache = class {
403
457
  get isLoaded() {
404
458
  return this._isLoaded;
405
459
  }
460
+ /**
461
+ * Protected method to load CloudAdapter class.
462
+ * Can be overridden/mocked for testing.
463
+ */
464
+ async _getCloudAdapterClass() {
465
+ const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
466
+ return CloudAdapter2;
467
+ }
406
468
  /**
407
469
  * Check if this instance is connected to cloud
408
470
  */
409
471
  get isCloud() {
410
472
  return this._cloudConfig !== null;
411
473
  }
474
+ /**
475
+ * Wait for initial sync to complete (or resolve immediately if already synced/local).
476
+ * Useful for scripts or linear execution flows.
477
+ */
478
+ async waitForSync() {
479
+ if (this._isLoaded) {
480
+ return;
481
+ }
482
+ if (this._initPromise) {
483
+ await this._initPromise;
484
+ }
485
+ if (this._isLoaded) {
486
+ return;
487
+ }
488
+ return new Promise((resolve) => {
489
+ if (!this._cloudAdapter) {
490
+ resolve();
491
+ return;
492
+ }
493
+ const handler = () => {
494
+ this._cloudAdapter?.off("synced", handler);
495
+ resolve();
496
+ };
497
+ this._cloudAdapter.on("synced", handler);
498
+ });
499
+ }
412
500
  /**
413
501
  * Disconnect from cloud (if connected)
414
502
  */
@@ -491,11 +579,16 @@ var MindCache = class {
491
579
  get_attributes(key) {
492
580
  if (key === "$date" || key === "$time") {
493
581
  return {
582
+ type: "text",
583
+ contentTags: [],
584
+ systemTags: ["prompt", "readonly", "protected"],
585
+ zIndex: 999999,
586
+ // System keys appear last
587
+ // Legacy attributes
494
588
  readonly: true,
495
589
  visible: true,
496
590
  hardcoded: true,
497
591
  template: false,
498
- type: "text",
499
592
  tags: []
500
593
  };
501
594
  }
@@ -508,18 +601,98 @@ var MindCache = class {
508
601
  return;
509
602
  }
510
603
  const existingEntry = this.stm[key];
511
- const baseAttributes = existingEntry ? existingEntry.attributes : { ...DEFAULT_KEY_ATTRIBUTES };
604
+ const wasHardcoded = existingEntry?.attributes.hardcoded || existingEntry?.attributes.systemTags?.includes("protected");
605
+ const baseAttributes = existingEntry ? {
606
+ ...existingEntry.attributes,
607
+ contentTags: [...existingEntry.attributes.contentTags || []],
608
+ systemTags: [...existingEntry.attributes.systemTags || []],
609
+ tags: [...existingEntry.attributes.tags || []],
610
+ zIndex: existingEntry.attributes.zIndex ?? 0
611
+ } : {
612
+ ...DEFAULT_KEY_ATTRIBUTES,
613
+ contentTags: [],
614
+ // Fresh array
615
+ systemTags: ["prompt"],
616
+ // Fresh array with default
617
+ tags: [],
618
+ // Fresh array
619
+ zIndex: 0
620
+ };
512
621
  const finalAttributes = attributes ? { ...baseAttributes, ...attributes } : baseAttributes;
513
- if (finalAttributes.hardcoded) {
514
- finalAttributes.readonly = true;
515
- finalAttributes.template = false;
622
+ if (attributes) {
623
+ let systemTags2 = [...finalAttributes.systemTags || []];
624
+ if ("readonly" in attributes) {
625
+ if (attributes.readonly && !systemTags2.includes("readonly")) {
626
+ systemTags2.push("readonly");
627
+ } else if (!attributes.readonly && !wasHardcoded) {
628
+ systemTags2 = systemTags2.filter((t) => t !== "readonly");
629
+ }
630
+ }
631
+ if ("visible" in attributes) {
632
+ if (attributes.visible && !systemTags2.includes("prompt")) {
633
+ systemTags2.push("prompt");
634
+ } else if (!attributes.visible) {
635
+ systemTags2 = systemTags2.filter((t) => t !== "prompt");
636
+ }
637
+ }
638
+ if ("hardcoded" in attributes) {
639
+ if (attributes.hardcoded && !systemTags2.includes("protected")) {
640
+ systemTags2.push("protected");
641
+ } else if (!attributes.hardcoded && !wasHardcoded) {
642
+ systemTags2 = systemTags2.filter((t) => t !== "protected");
643
+ }
644
+ if (wasHardcoded && !systemTags2.includes("protected")) {
645
+ systemTags2.push("protected");
646
+ }
647
+ } else if (wasHardcoded) {
648
+ if (!systemTags2.includes("protected")) {
649
+ systemTags2.push("protected");
650
+ }
651
+ }
652
+ if ("template" in attributes) {
653
+ if (attributes.template && !wasHardcoded && !systemTags2.includes("template")) {
654
+ systemTags2.push("template");
655
+ } else if (!attributes.template || wasHardcoded) {
656
+ systemTags2 = systemTags2.filter((t) => t !== "template");
657
+ }
658
+ }
659
+ finalAttributes.systemTags = systemTags2;
660
+ } else if (wasHardcoded) {
661
+ let systemTags2 = [...finalAttributes.systemTags || []];
662
+ if (!systemTags2.includes("protected")) {
663
+ systemTags2.push("protected");
664
+ }
665
+ if (!systemTags2.includes("readonly")) {
666
+ systemTags2.push("readonly");
667
+ }
668
+ systemTags2 = systemTags2.filter((t) => t !== "template");
669
+ finalAttributes.systemTags = systemTags2;
516
670
  }
671
+ let systemTags = finalAttributes.systemTags || [];
672
+ if (wasHardcoded && !systemTags.includes("protected")) {
673
+ systemTags = [...systemTags, "protected"];
674
+ }
675
+ if (systemTags.includes("protected")) {
676
+ if (!systemTags.includes("readonly")) {
677
+ systemTags = [...systemTags, "readonly"];
678
+ }
679
+ systemTags = systemTags.filter((t) => t !== "template");
680
+ finalAttributes.systemTags = systemTags;
681
+ }
682
+ finalAttributes.readonly = systemTags.includes("readonly");
683
+ finalAttributes.visible = systemTags.includes("prompt");
684
+ finalAttributes.hardcoded = wasHardcoded || systemTags.includes("protected");
685
+ finalAttributes.template = systemTags.includes("template");
686
+ if (attributes && "tags" in attributes && attributes.tags) {
687
+ finalAttributes.contentTags = [...attributes.tags];
688
+ }
689
+ finalAttributes.tags = [...finalAttributes.contentTags || []];
517
690
  this.stm[key] = {
518
691
  value,
519
692
  attributes: finalAttributes
520
693
  };
521
694
  if (this.listeners[key]) {
522
- this.listeners[key].forEach((listener) => listener());
695
+ this.listeners[key].forEach((listener) => listener(value));
523
696
  }
524
697
  this.notifyGlobalListeners();
525
698
  }
@@ -530,12 +703,38 @@ var MindCache = class {
530
703
  return;
531
704
  }
532
705
  this._isRemoteUpdate = true;
706
+ const systemTags = attributes.systemTags || [];
707
+ if (!attributes.systemTags) {
708
+ if (attributes.visible !== false) {
709
+ systemTags.push("prompt");
710
+ }
711
+ if (attributes.readonly) {
712
+ systemTags.push("readonly");
713
+ }
714
+ if (attributes.hardcoded) {
715
+ systemTags.push("protected");
716
+ }
717
+ if (attributes.template) {
718
+ systemTags.push("template");
719
+ }
720
+ }
721
+ const contentTags = attributes.contentTags || attributes.tags || [];
533
722
  this.stm[key] = {
534
723
  value,
535
- attributes
724
+ attributes: {
725
+ ...attributes,
726
+ contentTags,
727
+ systemTags,
728
+ zIndex: attributes.zIndex ?? 0,
729
+ tags: contentTags,
730
+ readonly: systemTags.includes("readonly"),
731
+ visible: systemTags.includes("prompt"),
732
+ hardcoded: systemTags.includes("protected"),
733
+ template: systemTags.includes("template")
734
+ }
536
735
  };
537
736
  if (this.listeners[key]) {
538
- this.listeners[key].forEach((listener) => listener());
737
+ this.listeners[key].forEach((listener) => listener(value));
539
738
  }
540
739
  this.notifyGlobalListeners();
541
740
  this._isRemoteUpdate = false;
@@ -553,7 +752,7 @@ var MindCache = class {
553
752
  if (key in this.stm) {
554
753
  delete this.stm[key];
555
754
  if (this.listeners[key]) {
556
- this.listeners[key].forEach((listener) => listener());
755
+ this.listeners[key].forEach((listener) => listener(void 0));
557
756
  }
558
757
  this.notifyGlobalListeners();
559
758
  }
@@ -575,12 +774,62 @@ var MindCache = class {
575
774
  if (!entry) {
576
775
  return false;
577
776
  }
578
- const { hardcoded: _hardcoded, ...allowedAttributes } = attributes;
777
+ const wasHardcoded = entry.attributes.hardcoded || entry.attributes.systemTags?.includes("protected");
778
+ const { hardcoded: _hardcoded, systemTags: _systemTags, ...allowedAttributes } = attributes;
779
+ if (wasHardcoded) {
780
+ if ("readonly" in allowedAttributes) {
781
+ delete allowedAttributes.readonly;
782
+ }
783
+ if ("template" in allowedAttributes) {
784
+ delete allowedAttributes.template;
785
+ }
786
+ }
579
787
  entry.attributes = { ...entry.attributes, ...allowedAttributes };
580
- if (entry.attributes.hardcoded) {
788
+ if ("readonly" in attributes || "visible" in attributes || "template" in attributes) {
789
+ let newSystemTags = [];
790
+ if (entry.attributes.readonly) {
791
+ newSystemTags.push("readonly");
792
+ }
793
+ if (entry.attributes.visible) {
794
+ newSystemTags.push("prompt");
795
+ }
796
+ if (entry.attributes.template) {
797
+ newSystemTags.push("template");
798
+ }
799
+ if (wasHardcoded || entry.attributes.hardcoded) {
800
+ newSystemTags.push("protected");
801
+ }
802
+ if (newSystemTags.includes("protected")) {
803
+ if (!newSystemTags.includes("readonly")) {
804
+ newSystemTags.push("readonly");
805
+ }
806
+ newSystemTags = newSystemTags.filter((t) => t !== "template");
807
+ entry.attributes.readonly = true;
808
+ entry.attributes.template = false;
809
+ }
810
+ entry.attributes.systemTags = newSystemTags;
811
+ } else if (wasHardcoded) {
812
+ let systemTags = [...entry.attributes.systemTags || []];
813
+ if (!systemTags.includes("protected")) {
814
+ systemTags.push("protected");
815
+ }
816
+ if (!systemTags.includes("readonly")) {
817
+ systemTags.push("readonly");
818
+ }
819
+ systemTags = systemTags.filter((t) => t !== "template");
820
+ entry.attributes.systemTags = systemTags;
821
+ }
822
+ if (wasHardcoded) {
823
+ entry.attributes.hardcoded = true;
824
+ if (!entry.attributes.systemTags?.includes("protected")) {
825
+ entry.attributes.systemTags = [...entry.attributes.systemTags || [], "protected"];
826
+ }
581
827
  entry.attributes.readonly = true;
582
828
  entry.attributes.template = false;
583
829
  }
830
+ if ("contentTags" in attributes) {
831
+ entry.attributes.tags = [...entry.attributes.contentTags || []];
832
+ }
584
833
  this.notifyGlobalListeners();
585
834
  return true;
586
835
  }
@@ -652,7 +901,7 @@ var MindCache = class {
652
901
  if (deleted) {
653
902
  this.notifyGlobalListeners();
654
903
  if (this.listeners[key]) {
655
- this.listeners[key].forEach((listener) => listener());
904
+ this.listeners[key].forEach((listener) => listener(void 0));
656
905
  }
657
906
  }
658
907
  return deleted;
@@ -661,12 +910,26 @@ var MindCache = class {
661
910
  this.stm = {};
662
911
  this.notifyGlobalListeners();
663
912
  }
913
+ /**
914
+ * Get keys sorted by zIndex (ascending), then by key name
915
+ */
916
+ getSortedKeys() {
917
+ return Object.entries(this.stm).sort(([keyA, entryA], [keyB, entryB]) => {
918
+ const zIndexA = entryA.attributes.zIndex ?? 0;
919
+ const zIndexB = entryB.attributes.zIndex ?? 0;
920
+ if (zIndexA !== zIndexB) {
921
+ return zIndexA - zIndexB;
922
+ }
923
+ return keyA.localeCompare(keyB);
924
+ }).map(([key]) => key);
925
+ }
664
926
  keys() {
665
- return [...Object.keys(this.stm), "$date", "$time"];
927
+ return [...this.getSortedKeys(), "$date", "$time"];
666
928
  }
667
929
  values() {
668
930
  const now = /* @__PURE__ */ new Date();
669
- const stmValues = Object.values(this.stm).map((entry) => entry.value);
931
+ const sortedKeys = this.getSortedKeys();
932
+ const stmValues = sortedKeys.map((key) => this.stm[key].value);
670
933
  return [
671
934
  ...stmValues,
672
935
  now.toISOString().split("T")[0],
@@ -675,8 +938,9 @@ var MindCache = class {
675
938
  }
676
939
  entries() {
677
940
  const now = /* @__PURE__ */ new Date();
678
- const stmEntries = Object.entries(this.stm).map(
679
- ([key, entry]) => [key, entry.value]
941
+ const sortedKeys = this.getSortedKeys();
942
+ const stmEntries = sortedKeys.map(
943
+ (key) => [key, this.stm[key].value]
680
944
  );
681
945
  return [
682
946
  ...stmEntries,
@@ -690,8 +954,9 @@ var MindCache = class {
690
954
  getAll() {
691
955
  const now = /* @__PURE__ */ new Date();
692
956
  const result = {};
693
- Object.entries(this.stm).forEach(([key, entry]) => {
694
- result[key] = entry.value;
957
+ const sortedKeys = this.getSortedKeys();
958
+ sortedKeys.forEach((key) => {
959
+ result[key] = this.stm[key].value;
695
960
  });
696
961
  result["$date"] = now.toISOString().split("T")[0];
697
962
  result["$time"] = now.toTimeString().split(" ")[0];
@@ -705,7 +970,7 @@ var MindCache = class {
705
970
  attributes: { ...DEFAULT_KEY_ATTRIBUTES }
706
971
  };
707
972
  if (this.listeners[key]) {
708
- this.listeners[key].forEach((listener) => listener());
973
+ this.listeners[key].forEach((listener) => listener(this.stm[key]?.value));
709
974
  }
710
975
  }
711
976
  });
@@ -774,7 +1039,9 @@ var MindCache = class {
774
1039
  getSTM() {
775
1040
  const now = /* @__PURE__ */ new Date();
776
1041
  const entries = [];
777
- Object.entries(this.stm).forEach(([key, entry]) => {
1042
+ const sortedKeys = this.getSortedKeys();
1043
+ sortedKeys.forEach((key) => {
1044
+ const entry = this.stm[key];
778
1045
  if (entry.attributes.visible) {
779
1046
  entries.push([key, this.get_value(key)]);
780
1047
  }
@@ -789,7 +1056,9 @@ var MindCache = class {
789
1056
  getSTMForAPI() {
790
1057
  const now = /* @__PURE__ */ new Date();
791
1058
  const apiData = [];
792
- Object.entries(this.stm).forEach(([key, entry]) => {
1059
+ const sortedKeys = this.getSortedKeys();
1060
+ sortedKeys.forEach((key) => {
1061
+ const entry = this.stm[key];
793
1062
  if (entry.attributes.visible) {
794
1063
  const processedValue = entry.attributes.template ? this.get_value(key) : entry.value;
795
1064
  apiData.push({
@@ -814,7 +1083,9 @@ var MindCache = class {
814
1083
  }
815
1084
  getVisibleImages() {
816
1085
  const imageParts = [];
817
- Object.entries(this.stm).forEach(([key, entry]) => {
1086
+ const sortedKeys = this.getSortedKeys();
1087
+ sortedKeys.forEach((key) => {
1088
+ const entry = this.stm[key];
818
1089
  if (entry.attributes.visible && entry.attributes.type === "image" && entry.attributes.contentType) {
819
1090
  const dataUrl = this.createDataUrl(entry.value, entry.attributes.contentType);
820
1091
  imageParts.push({
@@ -840,7 +1111,9 @@ var MindCache = class {
840
1111
  }
841
1112
  serialize() {
842
1113
  const result = {};
843
- Object.entries(this.stm).forEach(([key, entry]) => {
1114
+ const sortedKeys = this.getSortedKeys();
1115
+ sortedKeys.forEach((key) => {
1116
+ const entry = this.stm[key];
844
1117
  if (!entry.attributes.hardcoded) {
845
1118
  result[key] = {
846
1119
  value: entry.value,
@@ -855,11 +1128,40 @@ var MindCache = class {
855
1128
  this.clear();
856
1129
  Object.entries(data).forEach(([key, entry]) => {
857
1130
  if (entry && typeof entry === "object" && "value" in entry && "attributes" in entry) {
1131
+ const attrs = entry.attributes;
1132
+ if (attrs.hardcoded === true || attrs.systemTags?.includes("protected")) {
1133
+ return;
1134
+ }
1135
+ let systemTags = attrs.systemTags || [];
1136
+ if (!attrs.systemTags) {
1137
+ systemTags = [];
1138
+ if (attrs.visible !== false) {
1139
+ systemTags.push("prompt");
1140
+ }
1141
+ if (attrs.readonly) {
1142
+ systemTags.push("readonly");
1143
+ }
1144
+ if (attrs.hardcoded) {
1145
+ systemTags.push("protected");
1146
+ }
1147
+ if (attrs.template) {
1148
+ systemTags.push("template");
1149
+ }
1150
+ }
1151
+ const contentTags = attrs.contentTags || attrs.tags || [];
858
1152
  this.stm[key] = {
859
1153
  value: entry.value,
860
1154
  attributes: {
861
- ...entry.attributes,
862
- tags: entry.attributes.tags || []
1155
+ ...attrs,
1156
+ contentTags,
1157
+ systemTags,
1158
+ zIndex: attrs.zIndex ?? 0,
1159
+ // Sync legacy attributes
1160
+ tags: contentTags,
1161
+ readonly: systemTags.includes("readonly"),
1162
+ visible: systemTags.includes("prompt"),
1163
+ hardcoded: systemTags.includes("protected"),
1164
+ template: systemTags.includes("template")
863
1165
  }
864
1166
  };
865
1167
  }
@@ -870,7 +1172,9 @@ var MindCache = class {
870
1172
  get_system_prompt() {
871
1173
  const now = /* @__PURE__ */ new Date();
872
1174
  const promptLines = [];
873
- Object.entries(this.stm).forEach(([key, entry]) => {
1175
+ const sortedKeys = this.getSortedKeys();
1176
+ sortedKeys.forEach((key) => {
1177
+ const entry = this.stm[key];
874
1178
  if (entry.attributes.visible) {
875
1179
  if (entry.attributes.type === "image") {
876
1180
  promptLines.push(`image ${key} available`);
@@ -905,14 +1209,18 @@ var MindCache = class {
905
1209
  return void 0;
906
1210
  }
907
1211
  const sanitizedKey = toolName.replace("write_", "");
908
- const allKeys = Object.keys(this.stm);
909
- return allKeys.find(
1212
+ const sortedKeys = this.getSortedKeys();
1213
+ return sortedKeys.find(
910
1214
  (k) => k.replace(/[^a-zA-Z0-9_-]/g, "_") === sanitizedKey
911
1215
  );
912
1216
  }
913
1217
  get_aisdk_tools() {
914
1218
  const tools = {};
915
- const writableKeys = Object.entries(this.stm).filter(([, entry]) => !entry.attributes.readonly).map(([key]) => key);
1219
+ const sortedKeys = this.getSortedKeys();
1220
+ const writableKeys = sortedKeys.filter((key) => {
1221
+ const entry = this.stm[key];
1222
+ return !entry.attributes.readonly;
1223
+ });
916
1224
  writableKeys.forEach((key) => {
917
1225
  const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
918
1226
  const toolName = `write_${sanitizedKey}`;
@@ -996,6 +1304,12 @@ var MindCache = class {
996
1304
  value
997
1305
  };
998
1306
  }
1307
+ // ============================================
1308
+ // Content Tag Methods (available to all access levels)
1309
+ // ============================================
1310
+ /**
1311
+ * Add a content tag to a key (user-level organization)
1312
+ */
999
1313
  addTag(key, tag) {
1000
1314
  if (key === "$date" || key === "$time") {
1001
1315
  return false;
@@ -1004,64 +1318,231 @@ var MindCache = class {
1004
1318
  if (!entry) {
1005
1319
  return false;
1006
1320
  }
1007
- if (!entry.attributes.tags) {
1008
- entry.attributes.tags = [];
1321
+ if (!entry.attributes.contentTags) {
1322
+ entry.attributes.contentTags = [];
1009
1323
  }
1010
- if (!entry.attributes.tags.includes(tag)) {
1011
- entry.attributes.tags.push(tag);
1324
+ if (!entry.attributes.contentTags.includes(tag)) {
1325
+ entry.attributes.contentTags.push(tag);
1326
+ entry.attributes.tags = [...entry.attributes.contentTags];
1012
1327
  this.notifyGlobalListeners();
1013
1328
  return true;
1014
1329
  }
1015
1330
  return false;
1016
1331
  }
1332
+ /**
1333
+ * Remove a content tag from a key
1334
+ */
1017
1335
  removeTag(key, tag) {
1018
1336
  if (key === "$date" || key === "$time") {
1019
1337
  return false;
1020
1338
  }
1021
1339
  const entry = this.stm[key];
1022
- if (!entry || !entry.attributes.tags) {
1340
+ if (!entry || !entry.attributes.contentTags) {
1023
1341
  return false;
1024
1342
  }
1025
- const tagIndex = entry.attributes.tags.indexOf(tag);
1343
+ const tagIndex = entry.attributes.contentTags.indexOf(tag);
1026
1344
  if (tagIndex > -1) {
1027
- entry.attributes.tags.splice(tagIndex, 1);
1345
+ entry.attributes.contentTags.splice(tagIndex, 1);
1346
+ entry.attributes.tags = [...entry.attributes.contentTags];
1028
1347
  this.notifyGlobalListeners();
1029
1348
  return true;
1030
1349
  }
1031
1350
  return false;
1032
1351
  }
1352
+ /**
1353
+ * Get all content tags for a key
1354
+ */
1033
1355
  getTags(key) {
1034
1356
  if (key === "$date" || key === "$time") {
1035
1357
  return [];
1036
1358
  }
1037
1359
  const entry = this.stm[key];
1038
- return entry?.attributes.tags || [];
1360
+ return entry?.attributes.contentTags || [];
1039
1361
  }
1362
+ /**
1363
+ * Get all unique content tags across all keys
1364
+ */
1040
1365
  getAllTags() {
1041
1366
  const allTags = /* @__PURE__ */ new Set();
1042
1367
  Object.values(this.stm).forEach((entry) => {
1043
- if (entry.attributes.tags) {
1044
- entry.attributes.tags.forEach((tag) => allTags.add(tag));
1368
+ if (entry.attributes.contentTags) {
1369
+ entry.attributes.contentTags.forEach((tag) => allTags.add(tag));
1045
1370
  }
1046
1371
  });
1047
1372
  return Array.from(allTags);
1048
1373
  }
1374
+ /**
1375
+ * Check if a key has a specific content tag
1376
+ */
1049
1377
  hasTag(key, tag) {
1050
1378
  if (key === "$date" || key === "$time") {
1051
1379
  return false;
1052
1380
  }
1053
1381
  const entry = this.stm[key];
1054
- return entry?.attributes.tags?.includes(tag) || false;
1382
+ return entry?.attributes.contentTags?.includes(tag) || false;
1055
1383
  }
1384
+ /**
1385
+ * Get all keys with a specific content tag as formatted string
1386
+ */
1056
1387
  getTagged(tag) {
1057
1388
  const entries = [];
1058
- Object.entries(this.stm).forEach(([key, entry]) => {
1059
- if (entry.attributes.tags?.includes(tag)) {
1389
+ const sortedKeys = this.getSortedKeys();
1390
+ sortedKeys.forEach((key) => {
1391
+ const entry = this.stm[key];
1392
+ if (entry.attributes.contentTags?.includes(tag)) {
1060
1393
  entries.push([key, this.get_value(key)]);
1061
1394
  }
1062
1395
  });
1063
1396
  return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
1064
1397
  }
1398
+ /**
1399
+ * Get all keys with a specific content tag
1400
+ */
1401
+ getKeysByTag(tag) {
1402
+ const sortedKeys = this.getSortedKeys();
1403
+ return sortedKeys.filter((key) => {
1404
+ const entry = this.stm[key];
1405
+ return entry.attributes.contentTags?.includes(tag);
1406
+ });
1407
+ }
1408
+ // ============================================
1409
+ // System Tag Methods (requires system access level)
1410
+ // ============================================
1411
+ /**
1412
+ * Add a system tag to a key (requires system access)
1413
+ * System tags: 'prompt', 'readonly', 'protected', 'template'
1414
+ */
1415
+ systemAddTag(key, tag) {
1416
+ if (!this.hasSystemAccess) {
1417
+ console.warn("MindCache: systemAddTag requires system access level");
1418
+ return false;
1419
+ }
1420
+ if (key === "$date" || key === "$time") {
1421
+ return false;
1422
+ }
1423
+ const entry = this.stm[key];
1424
+ if (!entry) {
1425
+ return false;
1426
+ }
1427
+ if (!entry.attributes.systemTags) {
1428
+ entry.attributes.systemTags = [];
1429
+ }
1430
+ if (!entry.attributes.systemTags.includes(tag)) {
1431
+ entry.attributes.systemTags.push(tag);
1432
+ this.syncLegacyFromSystemTags(entry);
1433
+ this.notifyGlobalListeners();
1434
+ return true;
1435
+ }
1436
+ return false;
1437
+ }
1438
+ /**
1439
+ * Remove a system tag from a key (requires system access)
1440
+ */
1441
+ systemRemoveTag(key, tag) {
1442
+ if (!this.hasSystemAccess) {
1443
+ console.warn("MindCache: systemRemoveTag requires system access level");
1444
+ return false;
1445
+ }
1446
+ if (key === "$date" || key === "$time") {
1447
+ return false;
1448
+ }
1449
+ const entry = this.stm[key];
1450
+ if (!entry || !entry.attributes.systemTags) {
1451
+ return false;
1452
+ }
1453
+ const isHardcoded = entry.attributes.hardcoded || entry.attributes.systemTags.includes("protected");
1454
+ if (tag === "protected" && isHardcoded) {
1455
+ return false;
1456
+ }
1457
+ const tagIndex = entry.attributes.systemTags.indexOf(tag);
1458
+ if (tagIndex > -1) {
1459
+ entry.attributes.systemTags.splice(tagIndex, 1);
1460
+ this.syncLegacyFromSystemTags(entry);
1461
+ if (isHardcoded) {
1462
+ if (!entry.attributes.systemTags.includes("protected")) {
1463
+ entry.attributes.systemTags.push("protected");
1464
+ }
1465
+ entry.attributes.hardcoded = true;
1466
+ entry.attributes.readonly = true;
1467
+ entry.attributes.template = false;
1468
+ }
1469
+ this.notifyGlobalListeners();
1470
+ return true;
1471
+ }
1472
+ return false;
1473
+ }
1474
+ /**
1475
+ * Get all system tags for a key (requires system access)
1476
+ */
1477
+ systemGetTags(key) {
1478
+ if (!this.hasSystemAccess) {
1479
+ console.warn("MindCache: systemGetTags requires system access level");
1480
+ return [];
1481
+ }
1482
+ if (key === "$date" || key === "$time") {
1483
+ return [];
1484
+ }
1485
+ const entry = this.stm[key];
1486
+ return entry?.attributes.systemTags || [];
1487
+ }
1488
+ /**
1489
+ * Check if a key has a specific system tag (requires system access)
1490
+ */
1491
+ systemHasTag(key, tag) {
1492
+ if (!this.hasSystemAccess) {
1493
+ console.warn("MindCache: systemHasTag requires system access level");
1494
+ return false;
1495
+ }
1496
+ if (key === "$date" || key === "$time") {
1497
+ return false;
1498
+ }
1499
+ const entry = this.stm[key];
1500
+ return entry?.attributes.systemTags?.includes(tag) || false;
1501
+ }
1502
+ /**
1503
+ * Set all system tags for a key at once (requires system access)
1504
+ */
1505
+ systemSetTags(key, tags) {
1506
+ if (!this.hasSystemAccess) {
1507
+ console.warn("MindCache: systemSetTags requires system access level");
1508
+ return false;
1509
+ }
1510
+ if (key === "$date" || key === "$time") {
1511
+ return false;
1512
+ }
1513
+ const entry = this.stm[key];
1514
+ if (!entry) {
1515
+ return false;
1516
+ }
1517
+ entry.attributes.systemTags = [...tags];
1518
+ this.syncLegacyFromSystemTags(entry);
1519
+ this.notifyGlobalListeners();
1520
+ return true;
1521
+ }
1522
+ /**
1523
+ * Get all keys with a specific system tag (requires system access)
1524
+ */
1525
+ systemGetKeysByTag(tag) {
1526
+ if (!this.hasSystemAccess) {
1527
+ console.warn("MindCache: systemGetKeysByTag requires system access level");
1528
+ return [];
1529
+ }
1530
+ const sortedKeys = this.getSortedKeys();
1531
+ return sortedKeys.filter((key) => {
1532
+ const entry = this.stm[key];
1533
+ return entry.attributes.systemTags?.includes(tag);
1534
+ });
1535
+ }
1536
+ /**
1537
+ * Helper to sync legacy boolean attributes from system tags
1538
+ */
1539
+ syncLegacyFromSystemTags(entry) {
1540
+ const tags = entry.attributes.systemTags || [];
1541
+ entry.attributes.readonly = tags.includes("readonly");
1542
+ entry.attributes.visible = tags.includes("prompt");
1543
+ entry.attributes.hardcoded = tags.includes("protected");
1544
+ entry.attributes.template = tags.includes("template");
1545
+ }
1065
1546
  toMarkdown() {
1066
1547
  const now = /* @__PURE__ */ new Date();
1067
1548
  const lines = [];
@@ -1075,7 +1556,9 @@ var MindCache = class {
1075
1556
  lines.push("");
1076
1557
  lines.push("## STM Entries");
1077
1558
  lines.push("");
1078
- Object.entries(this.stm).forEach(([key, entry]) => {
1559
+ const sortedKeys = this.getSortedKeys();
1560
+ sortedKeys.forEach((key) => {
1561
+ const entry = this.stm[key];
1079
1562
  if (entry.attributes.hardcoded) {
1080
1563
  return;
1081
1564
  }
@@ -1085,6 +1568,7 @@ var MindCache = class {
1085
1568
  lines.push(`- **Readonly**: \`${entry.attributes.readonly}\``);
1086
1569
  lines.push(`- **Visible**: \`${entry.attributes.visible}\``);
1087
1570
  lines.push(`- **Template**: \`${entry.attributes.template}\``);
1571
+ lines.push(`- **Z-Index**: \`${entry.attributes.zIndex ?? 0}\``);
1088
1572
  if (entry.attributes.tags && entry.attributes.tags.length > 0) {
1089
1573
  lines.push(`- **Tags**: \`${entry.attributes.tags.join("`, `")}\``);
1090
1574
  }
@@ -1197,11 +1681,9 @@ var MindCache = class {
1197
1681
  currentEntry = {
1198
1682
  value: void 0,
1199
1683
  attributes: {
1200
- readonly: false,
1201
- visible: true,
1202
- hardcoded: false,
1203
- template: false,
1204
- type: "text",
1684
+ ...DEFAULT_KEY_ATTRIBUTES,
1685
+ contentTags: [],
1686
+ systemTags: ["prompt"],
1205
1687
  tags: []
1206
1688
  }
1207
1689
  };
@@ -1225,6 +1707,14 @@ var MindCache = class {
1225
1707
  if (currentEntry) {
1226
1708
  currentEntry.attributes.template = value;
1227
1709
  }
1710
+ } else if (trimmed.startsWith("- **Z-Index**: `")) {
1711
+ const zIndexStr = trimmed.match(/`([^`]+)`/)?.[1];
1712
+ if (currentEntry && zIndexStr) {
1713
+ const zIndex = parseInt(zIndexStr, 10);
1714
+ if (!isNaN(zIndex)) {
1715
+ currentEntry.attributes.zIndex = zIndex;
1716
+ }
1717
+ }
1228
1718
  } else if (trimmed.startsWith("- **Tags**: `")) {
1229
1719
  const tagsStr = trimmed.substring(13, trimmed.length - 1);
1230
1720
  if (currentEntry) {
@@ -1281,9 +1771,35 @@ var MindCache = class {
1281
1771
  }
1282
1772
  }
1283
1773
  if (entry.value !== void 0 && entry.attributes) {
1774
+ const attrs = entry.attributes;
1775
+ if (attrs.tags && attrs.tags.length > 0 && (!attrs.contentTags || attrs.contentTags.length === 0)) {
1776
+ attrs.contentTags = [...attrs.tags];
1777
+ }
1778
+ if (!attrs.systemTags || attrs.systemTags.length === 0) {
1779
+ const systemTags = [];
1780
+ if (attrs.visible !== false) {
1781
+ systemTags.push("prompt");
1782
+ }
1783
+ if (attrs.readonly) {
1784
+ systemTags.push("readonly");
1785
+ }
1786
+ if (attrs.hardcoded) {
1787
+ systemTags.push("protected");
1788
+ }
1789
+ if (attrs.template) {
1790
+ systemTags.push("template");
1791
+ }
1792
+ attrs.systemTags = systemTags;
1793
+ }
1794
+ if (!attrs.contentTags) {
1795
+ attrs.contentTags = [];
1796
+ }
1797
+ if (!attrs.tags) {
1798
+ attrs.tags = [...attrs.contentTags];
1799
+ }
1284
1800
  this.stm[key] = {
1285
1801
  value: entry.value,
1286
- attributes: entry.attributes
1802
+ attributes: attrs
1287
1803
  };
1288
1804
  }
1289
1805
  });