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