mindcache 2.4.1 → 3.1.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/{CloudAdapter-B04-OWx3.d.mts → CloudAdapter-Bx-ITNdi.d.mts} +132 -201
- package/dist/{CloudAdapter-B04-OWx3.d.ts → CloudAdapter-Bx-ITNdi.d.ts} +132 -201
- package/dist/cloud/index.d.mts +3 -2
- package/dist/cloud/index.d.ts +3 -2
- package/dist/cloud/index.js +786 -1469
- package/dist/cloud/index.js.map +1 -1
- package/dist/cloud/index.mjs +762 -1471
- package/dist/cloud/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +786 -1470
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +763 -1472
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var syncProtocol = require('y-protocols/sync');
|
|
4
|
+
var encoding = require('lib0/encoding');
|
|
5
|
+
var decoding = require('lib0/decoding');
|
|
6
|
+
var Y = require('yjs');
|
|
7
|
+
var yIndexeddb = require('y-indexeddb');
|
|
8
|
+
var diff = require('fast-diff');
|
|
4
9
|
var react = require('react');
|
|
5
10
|
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
function _interopNamespace(e) {
|
|
14
|
+
if (e && e.__esModule) return e;
|
|
15
|
+
var n = Object.create(null);
|
|
16
|
+
if (e) {
|
|
17
|
+
Object.keys(e).forEach(function (k) {
|
|
18
|
+
if (k !== 'default') {
|
|
19
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return e[k]; }
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
n.default = e;
|
|
28
|
+
return Object.freeze(n);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var syncProtocol__namespace = /*#__PURE__*/_interopNamespace(syncProtocol);
|
|
32
|
+
var encoding__namespace = /*#__PURE__*/_interopNamespace(encoding);
|
|
33
|
+
var decoding__namespace = /*#__PURE__*/_interopNamespace(decoding);
|
|
34
|
+
var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
|
|
35
|
+
var diff__default = /*#__PURE__*/_interopDefault(diff);
|
|
36
|
+
|
|
6
37
|
var __defProp = Object.defineProperty;
|
|
7
38
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
39
|
var __esm = (fn, res) => function __init() {
|
|
@@ -182,7 +213,6 @@ var init_CloudAdapter = __esm({
|
|
|
182
213
|
}
|
|
183
214
|
}
|
|
184
215
|
ws = null;
|
|
185
|
-
queue = [];
|
|
186
216
|
mindcache = null;
|
|
187
217
|
unsubscribe = null;
|
|
188
218
|
reconnectAttempts = 0;
|
|
@@ -190,49 +220,29 @@ var init_CloudAdapter = __esm({
|
|
|
190
220
|
_state = "disconnected";
|
|
191
221
|
listeners = {};
|
|
192
222
|
token = null;
|
|
193
|
-
/**
|
|
194
|
-
* Set auth token (short-lived, from /api/ws-token)
|
|
195
|
-
* Call this before connect() or use setTokenProvider for auto-refresh
|
|
196
|
-
*/
|
|
197
223
|
setToken(token) {
|
|
198
224
|
this.token = token;
|
|
199
225
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Set a function that returns a fresh token
|
|
202
|
-
* Used for automatic token refresh on reconnect
|
|
203
|
-
*/
|
|
204
226
|
setTokenProvider(provider) {
|
|
205
227
|
this.config.tokenProvider = provider;
|
|
206
228
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Get current connection state
|
|
209
|
-
*/
|
|
210
229
|
get state() {
|
|
211
230
|
return this._state;
|
|
212
231
|
}
|
|
213
|
-
/**
|
|
214
|
-
* Attach to a MindCache instance and start syncing
|
|
215
|
-
*/
|
|
216
232
|
attach(mc) {
|
|
217
233
|
if (this.mindcache) {
|
|
218
234
|
this.detach();
|
|
219
235
|
}
|
|
220
236
|
this.mindcache = mc;
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
237
|
+
mc.doc.on("update", (update, origin) => {
|
|
238
|
+
if (origin !== this && this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
239
|
+
const encoder = encoding__namespace.createEncoder();
|
|
240
|
+
syncProtocol__namespace.writeUpdate(encoder, update);
|
|
241
|
+
this.sendBinary(encoding__namespace.toUint8Array(encoder));
|
|
225
242
|
}
|
|
226
|
-
|
|
227
|
-
this.syncLocalChanges();
|
|
228
|
-
};
|
|
229
|
-
mc.subscribeToAll(listener);
|
|
230
|
-
this.unsubscribe = () => mc.unsubscribeFromAll(listener);
|
|
243
|
+
});
|
|
231
244
|
console.log("\u2601\uFE0F CloudAdapter: Attached to MindCache instance");
|
|
232
245
|
}
|
|
233
|
-
/**
|
|
234
|
-
* Detach from the MindCache instance
|
|
235
|
-
*/
|
|
236
246
|
detach() {
|
|
237
247
|
if (this.unsubscribe) {
|
|
238
248
|
this.unsubscribe();
|
|
@@ -240,15 +250,6 @@ var init_CloudAdapter = __esm({
|
|
|
240
250
|
}
|
|
241
251
|
this.mindcache = null;
|
|
242
252
|
}
|
|
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
253
|
async fetchTokenWithApiKey() {
|
|
253
254
|
if (!this.config.apiKey) {
|
|
254
255
|
throw new Error("API key is required to fetch token");
|
|
@@ -274,9 +275,6 @@ var init_CloudAdapter = __esm({
|
|
|
274
275
|
const data = await response.json();
|
|
275
276
|
return data.token;
|
|
276
277
|
}
|
|
277
|
-
/**
|
|
278
|
-
* Connect to the cloud service
|
|
279
|
-
*/
|
|
280
278
|
async connect() {
|
|
281
279
|
if (this._state === "connecting" || this._state === "connected") {
|
|
282
280
|
return;
|
|
@@ -298,6 +296,7 @@ var init_CloudAdapter = __esm({
|
|
|
298
296
|
throw new Error("MindCache Cloud: No authentication method available. Provide apiKey or tokenProvider.");
|
|
299
297
|
}
|
|
300
298
|
this.ws = new WebSocket(url);
|
|
299
|
+
this.ws.binaryType = "arraybuffer";
|
|
301
300
|
this.setupWebSocket();
|
|
302
301
|
} catch (error) {
|
|
303
302
|
this._state = "error";
|
|
@@ -305,9 +304,6 @@ var init_CloudAdapter = __esm({
|
|
|
305
304
|
this.scheduleReconnect();
|
|
306
305
|
}
|
|
307
306
|
}
|
|
308
|
-
/**
|
|
309
|
-
* Disconnect from the cloud service
|
|
310
|
-
*/
|
|
311
307
|
disconnect() {
|
|
312
308
|
if (this.reconnectTimeout) {
|
|
313
309
|
clearTimeout(this.reconnectTimeout);
|
|
@@ -320,28 +316,12 @@ var init_CloudAdapter = __esm({
|
|
|
320
316
|
this._state = "disconnected";
|
|
321
317
|
this.emit("disconnected");
|
|
322
318
|
}
|
|
323
|
-
/**
|
|
324
|
-
* Push an operation to the cloud
|
|
325
|
-
*/
|
|
326
|
-
push(op) {
|
|
327
|
-
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
328
|
-
this.ws.send(JSON.stringify(op));
|
|
329
|
-
} else {
|
|
330
|
-
this.queue.push(op);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Add event listener
|
|
335
|
-
*/
|
|
336
319
|
on(event, listener) {
|
|
337
320
|
if (!this.listeners[event]) {
|
|
338
321
|
this.listeners[event] = [];
|
|
339
322
|
}
|
|
340
323
|
this.listeners[event].push(listener);
|
|
341
324
|
}
|
|
342
|
-
/**
|
|
343
|
-
* Remove event listener
|
|
344
|
-
*/
|
|
345
325
|
off(event, listener) {
|
|
346
326
|
if (this.listeners[event]) {
|
|
347
327
|
this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
|
|
@@ -357,14 +337,36 @@ var init_CloudAdapter = __esm({
|
|
|
357
337
|
return;
|
|
358
338
|
}
|
|
359
339
|
this.ws.onopen = () => {
|
|
340
|
+
if (this.mindcache) {
|
|
341
|
+
const encoder = encoding__namespace.createEncoder();
|
|
342
|
+
syncProtocol__namespace.writeSyncStep1(encoder, this.mindcache.doc);
|
|
343
|
+
this.sendBinary(encoding__namespace.toUint8Array(encoder));
|
|
344
|
+
}
|
|
360
345
|
};
|
|
361
346
|
this.ws.onmessage = (event) => {
|
|
362
347
|
try {
|
|
363
|
-
|
|
364
|
-
|
|
348
|
+
if (typeof event.data === "string") {
|
|
349
|
+
const msg = JSON.parse(event.data);
|
|
350
|
+
if (msg.type === "auth_success") {
|
|
351
|
+
this._state = "connected";
|
|
352
|
+
this.reconnectAttempts = 0;
|
|
353
|
+
this.emit("connected");
|
|
354
|
+
} else if (msg.type === "auth_error" || msg.type === "error") {
|
|
355
|
+
this._state = "error";
|
|
356
|
+
this.emit("error", new Error(msg.error));
|
|
357
|
+
}
|
|
358
|
+
} else {
|
|
359
|
+
const encoder = encoding__namespace.createEncoder();
|
|
360
|
+
const decoder = decoding__namespace.createDecoder(new Uint8Array(event.data));
|
|
361
|
+
if (this.mindcache) {
|
|
362
|
+
syncProtocol__namespace.readSyncMessage(decoder, encoder, this.mindcache.doc, this);
|
|
363
|
+
if (encoding__namespace.length(encoder) > 0) {
|
|
364
|
+
this.sendBinary(encoding__namespace.toUint8Array(encoder));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
365
368
|
} catch (error) {
|
|
366
|
-
console.error("MindCache Cloud: Failed to
|
|
367
|
-
console.error("Raw message:", typeof event.data === "string" ? event.data.slice(0, 200) : event.data);
|
|
369
|
+
console.error("MindCache Cloud: Failed to handle message:", error);
|
|
368
370
|
}
|
|
369
371
|
};
|
|
370
372
|
this.ws.onclose = () => {
|
|
@@ -374,73 +376,12 @@ var init_CloudAdapter = __esm({
|
|
|
374
376
|
};
|
|
375
377
|
this.ws.onerror = () => {
|
|
376
378
|
this._state = "error";
|
|
377
|
-
|
|
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}`));
|
|
379
|
+
this.emit("error", new Error("WebSocket connection failed"));
|
|
381
380
|
};
|
|
382
381
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
this._state = "connected";
|
|
387
|
-
this.reconnectAttempts = 0;
|
|
388
|
-
this.emit("connected");
|
|
389
|
-
this.flushQueue();
|
|
390
|
-
break;
|
|
391
|
-
case "auth_error":
|
|
392
|
-
this._state = "error";
|
|
393
|
-
this.emit("error", new Error(msg.error));
|
|
394
|
-
this.disconnect();
|
|
395
|
-
break;
|
|
396
|
-
case "sync":
|
|
397
|
-
if (this.mindcache && msg.data) {
|
|
398
|
-
Object.entries(msg.data).forEach(([key, entry]) => {
|
|
399
|
-
const { value, attributes } = entry;
|
|
400
|
-
this.mindcache._setFromRemote(key, value, attributes);
|
|
401
|
-
});
|
|
402
|
-
this.emit("synced");
|
|
403
|
-
}
|
|
404
|
-
break;
|
|
405
|
-
case "set":
|
|
406
|
-
if (this.mindcache) {
|
|
407
|
-
this.mindcache._setFromRemote(msg.key, msg.value, msg.attributes);
|
|
408
|
-
}
|
|
409
|
-
break;
|
|
410
|
-
case "key_updated":
|
|
411
|
-
if (this.mindcache) {
|
|
412
|
-
this.mindcache._setFromRemote(msg.key, msg.value, msg.attributes);
|
|
413
|
-
}
|
|
414
|
-
break;
|
|
415
|
-
case "delete":
|
|
416
|
-
if (this.mindcache) {
|
|
417
|
-
this.mindcache._deleteFromRemote(msg.key);
|
|
418
|
-
}
|
|
419
|
-
break;
|
|
420
|
-
case "key_deleted":
|
|
421
|
-
if (this.mindcache) {
|
|
422
|
-
this.mindcache._deleteFromRemote(msg.key);
|
|
423
|
-
}
|
|
424
|
-
break;
|
|
425
|
-
case "clear":
|
|
426
|
-
if (this.mindcache) {
|
|
427
|
-
this.mindcache._clearFromRemote();
|
|
428
|
-
}
|
|
429
|
-
break;
|
|
430
|
-
case "cleared":
|
|
431
|
-
if (this.mindcache) {
|
|
432
|
-
this.mindcache._clearFromRemote();
|
|
433
|
-
}
|
|
434
|
-
break;
|
|
435
|
-
case "error":
|
|
436
|
-
this.emit("error", new Error(msg.error));
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
flushQueue() {
|
|
441
|
-
while (this.queue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
|
|
442
|
-
const op = this.queue.shift();
|
|
443
|
-
this.ws.send(JSON.stringify(op));
|
|
382
|
+
sendBinary(data) {
|
|
383
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
384
|
+
this.ws.send(data);
|
|
444
385
|
}
|
|
445
386
|
}
|
|
446
387
|
scheduleReconnect() {
|
|
@@ -457,23 +398,6 @@ var init_CloudAdapter = __esm({
|
|
|
457
398
|
this.connect();
|
|
458
399
|
}, delay);
|
|
459
400
|
}
|
|
460
|
-
syncLocalChanges() {
|
|
461
|
-
if (!this.mindcache) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
const entries = this.mindcache.serialize();
|
|
465
|
-
console.log("\u2601\uFE0F CloudAdapter: Syncing local changes:", Object.keys(entries));
|
|
466
|
-
Object.entries(entries).forEach(([key, entry]) => {
|
|
467
|
-
console.log("\u2601\uFE0F CloudAdapter: Pushing key:", key, "=", entry.value);
|
|
468
|
-
this.push({
|
|
469
|
-
type: "set",
|
|
470
|
-
key,
|
|
471
|
-
value: entry.value,
|
|
472
|
-
attributes: entry.attributes,
|
|
473
|
-
timestamp: Date.now()
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
401
|
};
|
|
478
402
|
}
|
|
479
403
|
});
|
|
@@ -495,16 +419,18 @@ var DEFAULT_KEY_ATTRIBUTES = {
|
|
|
495
419
|
|
|
496
420
|
// src/core/MindCache.ts
|
|
497
421
|
var MindCache = class {
|
|
498
|
-
|
|
422
|
+
// Public doc for adapter access
|
|
423
|
+
doc;
|
|
424
|
+
rootMap;
|
|
425
|
+
// Key -> EntryMap({value, attributes})
|
|
426
|
+
// Cache listeners
|
|
499
427
|
listeners = {};
|
|
500
428
|
globalListeners = [];
|
|
429
|
+
// Metadata
|
|
430
|
+
version = "3.1.0";
|
|
501
431
|
// Internal flag to prevent sync loops when receiving remote updates
|
|
432
|
+
// (Less critical with Yjs but kept for API compat)
|
|
502
433
|
_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
434
|
normalizeSystemTags(tags) {
|
|
509
435
|
const normalized = [];
|
|
510
436
|
let hasSystemPrompt = false;
|
|
@@ -541,24 +467,6 @@ var MindCache = class {
|
|
|
541
467
|
}
|
|
542
468
|
return normalized;
|
|
543
469
|
}
|
|
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
|
-
}
|
|
562
470
|
// Cloud sync state
|
|
563
471
|
_cloudAdapter = null;
|
|
564
472
|
_connectionState = "disconnected";
|
|
@@ -568,25 +476,57 @@ var MindCache = class {
|
|
|
568
476
|
// Access level for system operations
|
|
569
477
|
_accessLevel = "user";
|
|
570
478
|
_initPromise = null;
|
|
479
|
+
// Y-IndexedDB provider
|
|
480
|
+
_idbProvider = null;
|
|
481
|
+
// Undo Managers Cache
|
|
482
|
+
_undoManagers = /* @__PURE__ */ new Map();
|
|
483
|
+
// Global Undo Manager (watches entire rootMap)
|
|
484
|
+
_globalUndoManager = null;
|
|
485
|
+
// History tracking
|
|
486
|
+
_history = [];
|
|
487
|
+
_historyOptions = { maxEntries: 100, snapshotInterval: 10 };
|
|
488
|
+
_historyEnabled = false;
|
|
571
489
|
constructor(options) {
|
|
490
|
+
this.doc = new Y__namespace.Doc();
|
|
491
|
+
this.rootMap = this.doc.getMap("mindcache");
|
|
492
|
+
this.rootMap.observe((event) => {
|
|
493
|
+
event.keysChanged.forEach((key) => {
|
|
494
|
+
const entryMap = this.rootMap.get(key);
|
|
495
|
+
if (entryMap) {
|
|
496
|
+
const value = entryMap.get("value");
|
|
497
|
+
if (this.listeners[key]) {
|
|
498
|
+
this.listeners[key].forEach((l) => l(value));
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
if (this.listeners[key]) {
|
|
502
|
+
this.listeners[key].forEach((l) => l(void 0));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
this.rootMap.observeDeep((_events) => {
|
|
508
|
+
this.notifyGlobalListeners();
|
|
509
|
+
});
|
|
510
|
+
this.initGlobalUndoManager();
|
|
572
511
|
if (options?.accessLevel) {
|
|
573
512
|
this._accessLevel = options.accessLevel;
|
|
574
513
|
}
|
|
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
514
|
const initPromises = [];
|
|
581
515
|
if (options?.cloud) {
|
|
582
516
|
this._cloudConfig = options.cloud;
|
|
583
517
|
this._isLoaded = false;
|
|
584
518
|
this._connectionState = "disconnected";
|
|
585
519
|
initPromises.push(this._initCloud());
|
|
520
|
+
if (typeof window !== "undefined") {
|
|
521
|
+
const dbName = `mindcache_cloud_${options.cloud.instanceId}`;
|
|
522
|
+
initPromises.push(this._initYIndexedDB(dbName));
|
|
523
|
+
}
|
|
524
|
+
this.enableHistory(options.history);
|
|
586
525
|
}
|
|
587
|
-
if (options?.indexedDB) {
|
|
526
|
+
if (options?.indexedDB && !options?.cloud) {
|
|
588
527
|
this._isLoaded = false;
|
|
589
|
-
initPromises.push(this.
|
|
528
|
+
initPromises.push(this._initYIndexedDB(options.indexedDB.dbName || "mindcache_yjs_db"));
|
|
529
|
+
this.enableHistory(options.history);
|
|
590
530
|
}
|
|
591
531
|
if (initPromises.length > 0) {
|
|
592
532
|
this._initPromise = Promise.all(initPromises).then(() => {
|
|
@@ -596,15 +536,147 @@ var MindCache = class {
|
|
|
596
536
|
});
|
|
597
537
|
}
|
|
598
538
|
}
|
|
539
|
+
// Helper: Get or Create UndoManager for a key
|
|
540
|
+
getUndoManager(key) {
|
|
541
|
+
const entryMap = this.rootMap.get(key);
|
|
542
|
+
if (!entryMap) {
|
|
543
|
+
return void 0;
|
|
544
|
+
}
|
|
545
|
+
if (!this._undoManagers.has(key)) {
|
|
546
|
+
const um = new Y__namespace.UndoManager(entryMap, {
|
|
547
|
+
captureTimeout: 500
|
|
548
|
+
});
|
|
549
|
+
this._undoManagers.set(key, um);
|
|
550
|
+
}
|
|
551
|
+
return this._undoManagers.get(key);
|
|
552
|
+
}
|
|
599
553
|
/**
|
|
600
|
-
*
|
|
554
|
+
* Undo changes for a specific key
|
|
601
555
|
*/
|
|
602
|
-
|
|
603
|
-
|
|
556
|
+
undo(key) {
|
|
557
|
+
const um = this.getUndoManager(key);
|
|
558
|
+
if (um) {
|
|
559
|
+
um.undo();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Redo changes for a specific key
|
|
564
|
+
*/
|
|
565
|
+
redo(key) {
|
|
566
|
+
const um = this.getUndoManager(key);
|
|
567
|
+
if (um) {
|
|
568
|
+
um.redo();
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
getHistory(key) {
|
|
572
|
+
const um = this.getUndoManager(key);
|
|
573
|
+
if (!um) {
|
|
574
|
+
return [];
|
|
575
|
+
}
|
|
576
|
+
return um.undoStack;
|
|
577
|
+
}
|
|
578
|
+
// Initialize global undo manager (watches entire rootMap)
|
|
579
|
+
initGlobalUndoManager() {
|
|
580
|
+
if (!this._globalUndoManager) {
|
|
581
|
+
this._globalUndoManager = new Y__namespace.UndoManager(this.rootMap, {
|
|
582
|
+
captureTimeout: 500
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Undo all recent local changes (across all keys)
|
|
588
|
+
* Only undoes YOUR changes, not changes from other users in cloud mode
|
|
589
|
+
*/
|
|
590
|
+
undoAll() {
|
|
591
|
+
this.initGlobalUndoManager();
|
|
592
|
+
this._globalUndoManager?.undo();
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Redo previously undone local changes
|
|
596
|
+
*/
|
|
597
|
+
redoAll() {
|
|
598
|
+
this.initGlobalUndoManager();
|
|
599
|
+
this._globalUndoManager?.redo();
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Check if there are changes to undo globally
|
|
603
|
+
*/
|
|
604
|
+
canUndoAll() {
|
|
605
|
+
this.initGlobalUndoManager();
|
|
606
|
+
return (this._globalUndoManager?.undoStack.length ?? 0) > 0;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Check if there are changes to redo globally
|
|
610
|
+
*/
|
|
611
|
+
canRedoAll() {
|
|
612
|
+
this.initGlobalUndoManager();
|
|
613
|
+
return (this._globalUndoManager?.redoStack.length ?? 0) > 0;
|
|
614
|
+
}
|
|
615
|
+
// Enable history tracking (called for IndexedDB and Cloud modes)
|
|
616
|
+
enableHistory(options) {
|
|
617
|
+
if (this._historyEnabled) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
this._historyEnabled = true;
|
|
621
|
+
if (options) {
|
|
622
|
+
this._historyOptions = { ...this._historyOptions, ...options };
|
|
623
|
+
}
|
|
624
|
+
this.rootMap.observeDeep((events) => {
|
|
625
|
+
const keysAffected = /* @__PURE__ */ new Set();
|
|
626
|
+
events.forEach((event) => {
|
|
627
|
+
if (event.target === this.rootMap) {
|
|
628
|
+
const mapEvent = event;
|
|
629
|
+
mapEvent.keysChanged.forEach((key) => keysAffected.add(key));
|
|
630
|
+
} else if (event.target.parent === this.rootMap) {
|
|
631
|
+
for (const [key, val] of this.rootMap) {
|
|
632
|
+
if (val === event.target) {
|
|
633
|
+
keysAffected.add(key);
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
if (keysAffected.size > 0) {
|
|
640
|
+
const entry = {
|
|
641
|
+
id: this.generateId(),
|
|
642
|
+
timestamp: Date.now(),
|
|
643
|
+
keysAffected: Array.from(keysAffected)
|
|
644
|
+
};
|
|
645
|
+
this._history.push(entry);
|
|
646
|
+
const max = this._historyOptions.maxEntries || 100;
|
|
647
|
+
if (this._history.length > max) {
|
|
648
|
+
this._history = this._history.slice(-max);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
generateId() {
|
|
654
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Get global history of all changes (available in IndexedDB and Cloud modes)
|
|
658
|
+
*/
|
|
659
|
+
getGlobalHistory() {
|
|
660
|
+
return [...this._history];
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Check if history tracking is enabled
|
|
664
|
+
*/
|
|
665
|
+
get historyEnabled() {
|
|
666
|
+
return this._historyEnabled;
|
|
604
667
|
}
|
|
605
668
|
/**
|
|
606
|
-
*
|
|
669
|
+
* Restore to a specific version (time travel)
|
|
670
|
+
* Note: Full implementation requires storing update binaries, which is not yet implemented.
|
|
671
|
+
* @returns false - not yet fully implemented
|
|
607
672
|
*/
|
|
673
|
+
restoreToVersion(_versionId) {
|
|
674
|
+
console.warn("restoreToVersion: Full implementation requires storing update binaries. Not yet implemented.");
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
get accessLevel() {
|
|
678
|
+
return this._accessLevel;
|
|
679
|
+
}
|
|
608
680
|
get hasSystemAccess() {
|
|
609
681
|
return this._accessLevel === "system";
|
|
610
682
|
}
|
|
@@ -612,110 +684,92 @@ var MindCache = class {
|
|
|
612
684
|
if (!this._cloudConfig) {
|
|
613
685
|
return;
|
|
614
686
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
687
|
+
const CloudAdapter2 = await this._getCloudAdapterClass();
|
|
688
|
+
if (!this._cloudConfig.baseUrl) ;
|
|
689
|
+
const baseUrl = (this._cloudConfig.baseUrl || "https://api.mindcache.io").replace("https://", "wss://").replace("http://", "ws://");
|
|
690
|
+
const adapter = new CloudAdapter2({
|
|
691
|
+
instanceId: this._cloudConfig.instanceId,
|
|
692
|
+
projectId: this._cloudConfig.projectId || "default",
|
|
693
|
+
baseUrl,
|
|
694
|
+
apiKey: this._cloudConfig.apiKey
|
|
695
|
+
});
|
|
696
|
+
if (this._cloudConfig.tokenEndpoint) {
|
|
697
|
+
const tokenEndpoint = this._cloudConfig.tokenEndpoint;
|
|
698
|
+
const instanceId = this._cloudConfig.instanceId;
|
|
699
|
+
let resolvedBaseUrl;
|
|
700
|
+
if (tokenEndpoint.startsWith("http://") || tokenEndpoint.startsWith("https://")) {
|
|
701
|
+
resolvedBaseUrl = tokenEndpoint;
|
|
702
|
+
} else if (typeof window !== "undefined" && window.location?.origin) {
|
|
703
|
+
resolvedBaseUrl = `${window.location.origin}${tokenEndpoint.startsWith("/") ? "" : "/"}${tokenEndpoint}`;
|
|
704
|
+
} else {
|
|
705
|
+
resolvedBaseUrl = tokenEndpoint;
|
|
619
706
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
apiKey: this._cloudConfig.apiKey
|
|
626
|
-
});
|
|
627
|
-
if (this._cloudConfig.tokenEndpoint) {
|
|
628
|
-
const tokenEndpoint = this._cloudConfig.tokenEndpoint;
|
|
629
|
-
const instanceId = this._cloudConfig.instanceId;
|
|
630
|
-
let resolvedBaseUrl;
|
|
631
|
-
if (tokenEndpoint.startsWith("http://") || tokenEndpoint.startsWith("https://")) {
|
|
632
|
-
resolvedBaseUrl = tokenEndpoint;
|
|
633
|
-
} else if (typeof window !== "undefined" && window.location?.origin) {
|
|
634
|
-
resolvedBaseUrl = `${window.location.origin}${tokenEndpoint.startsWith("/") ? "" : "/"}${tokenEndpoint}`;
|
|
635
|
-
} else {
|
|
636
|
-
console.warn("MindCache: Cannot resolve tokenEndpoint to absolute URL - window.location not available");
|
|
637
|
-
resolvedBaseUrl = tokenEndpoint;
|
|
707
|
+
adapter.setTokenProvider(async () => {
|
|
708
|
+
const url = resolvedBaseUrl.includes("?") ? `${resolvedBaseUrl}&instanceId=${instanceId}` : `${resolvedBaseUrl}?instanceId=${instanceId}`;
|
|
709
|
+
const response = await fetch(url);
|
|
710
|
+
if (!response.ok) {
|
|
711
|
+
throw new Error("Failed to get token");
|
|
638
712
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const response = await fetch(url);
|
|
642
|
-
if (!response.ok) {
|
|
643
|
-
const error = await response.json().catch(() => ({ error: "Failed to get token" }));
|
|
644
|
-
throw new Error(error.error || "Failed to get token");
|
|
645
|
-
}
|
|
646
|
-
const data = await response.json();
|
|
647
|
-
return data.token;
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
adapter.on("connected", () => {
|
|
651
|
-
this._connectionState = "connected";
|
|
652
|
-
this.notifyGlobalListeners();
|
|
653
|
-
});
|
|
654
|
-
adapter.on("disconnected", () => {
|
|
655
|
-
this._connectionState = "disconnected";
|
|
656
|
-
this.notifyGlobalListeners();
|
|
657
|
-
});
|
|
658
|
-
adapter.on("error", () => {
|
|
659
|
-
this._connectionState = "error";
|
|
660
|
-
this.notifyGlobalListeners();
|
|
661
|
-
});
|
|
662
|
-
adapter.on("synced", () => {
|
|
663
|
-
this._isLoaded = true;
|
|
664
|
-
this.notifyGlobalListeners();
|
|
713
|
+
const data = await response.json();
|
|
714
|
+
return data.token;
|
|
665
715
|
});
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
this._connectionState = "
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
716
|
+
}
|
|
717
|
+
adapter.on("connected", () => {
|
|
718
|
+
this._connectionState = "connected";
|
|
719
|
+
this.notifyGlobalListeners();
|
|
720
|
+
});
|
|
721
|
+
adapter.on("disconnected", () => {
|
|
722
|
+
this._connectionState = "disconnected";
|
|
723
|
+
this.notifyGlobalListeners();
|
|
724
|
+
});
|
|
725
|
+
adapter.on("error", () => {
|
|
672
726
|
this._connectionState = "error";
|
|
727
|
+
this.notifyGlobalListeners();
|
|
728
|
+
});
|
|
729
|
+
adapter.on("synced", () => {
|
|
673
730
|
this._isLoaded = true;
|
|
674
|
-
|
|
731
|
+
this.notifyGlobalListeners();
|
|
732
|
+
});
|
|
733
|
+
adapter.attach(this);
|
|
734
|
+
this._cloudAdapter = adapter;
|
|
735
|
+
this._connectionState = "connecting";
|
|
736
|
+
adapter.connect();
|
|
675
737
|
}
|
|
676
|
-
async
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
const adapter = new IndexedDBAdapter2(config);
|
|
680
|
-
await adapter.attach(this);
|
|
681
|
-
} catch (error) {
|
|
682
|
-
console.error("MindCache: Failed to initialize IndexedDB:", error);
|
|
738
|
+
async _initYIndexedDB(dbName) {
|
|
739
|
+
if (typeof window === "undefined") {
|
|
740
|
+
return;
|
|
683
741
|
}
|
|
742
|
+
this._idbProvider = new yIndexeddb.IndexeddbPersistence(dbName, this.doc);
|
|
743
|
+
return new Promise((resolve) => {
|
|
744
|
+
if (!this._idbProvider) {
|
|
745
|
+
return resolve();
|
|
746
|
+
}
|
|
747
|
+
this._idbProvider.on("synced", () => {
|
|
748
|
+
this._isLoaded = true;
|
|
749
|
+
resolve();
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
// Legacy IndexedDB method stub
|
|
754
|
+
async _initIndexedDB(_config) {
|
|
684
755
|
}
|
|
685
756
|
async _getIndexedDBAdapterClass() {
|
|
686
757
|
const { IndexedDBAdapter: IndexedDBAdapter2 } = await Promise.resolve().then(() => (init_IndexedDBAdapter(), IndexedDBAdapter_exports));
|
|
687
758
|
return IndexedDBAdapter2;
|
|
688
759
|
}
|
|
689
|
-
/**
|
|
690
|
-
* Get the current cloud connection state
|
|
691
|
-
*/
|
|
692
760
|
get connectionState() {
|
|
693
761
|
return this._connectionState;
|
|
694
762
|
}
|
|
695
|
-
/**
|
|
696
|
-
* Check if data is loaded (true for local, true after sync for cloud)
|
|
697
|
-
*/
|
|
698
763
|
get isLoaded() {
|
|
699
764
|
return this._isLoaded;
|
|
700
765
|
}
|
|
701
|
-
/**
|
|
702
|
-
* Protected method to load CloudAdapter class.
|
|
703
|
-
* Can be overridden/mocked for testing.
|
|
704
|
-
*/
|
|
705
766
|
async _getCloudAdapterClass() {
|
|
706
767
|
const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
|
|
707
768
|
return CloudAdapter2;
|
|
708
769
|
}
|
|
709
|
-
/**
|
|
710
|
-
* Check if this instance is connected to cloud
|
|
711
|
-
*/
|
|
712
770
|
get isCloud() {
|
|
713
771
|
return this._cloudConfig !== null;
|
|
714
772
|
}
|
|
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
773
|
async waitForSync() {
|
|
720
774
|
if (this._isLoaded) {
|
|
721
775
|
return;
|
|
@@ -727,20 +781,17 @@ var MindCache = class {
|
|
|
727
781
|
return;
|
|
728
782
|
}
|
|
729
783
|
return new Promise((resolve) => {
|
|
730
|
-
if (
|
|
731
|
-
resolve();
|
|
732
|
-
return;
|
|
784
|
+
if (this._isLoaded) {
|
|
785
|
+
return resolve();
|
|
733
786
|
}
|
|
734
|
-
const
|
|
735
|
-
this.
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
787
|
+
const interval = setInterval(() => {
|
|
788
|
+
if (this._isLoaded) {
|
|
789
|
+
clearInterval(interval);
|
|
790
|
+
resolve();
|
|
791
|
+
}
|
|
792
|
+
}, 100);
|
|
739
793
|
});
|
|
740
794
|
}
|
|
741
|
-
/**
|
|
742
|
-
* Disconnect from cloud (if connected)
|
|
743
|
-
*/
|
|
744
795
|
disconnect() {
|
|
745
796
|
if (this._cloudAdapter) {
|
|
746
797
|
this._cloudAdapter.disconnect();
|
|
@@ -748,8 +799,49 @@ var MindCache = class {
|
|
|
748
799
|
this._cloudAdapter = null;
|
|
749
800
|
this._connectionState = "disconnected";
|
|
750
801
|
}
|
|
802
|
+
if (this._idbProvider) {
|
|
803
|
+
this._idbProvider.destroy();
|
|
804
|
+
this._idbProvider = null;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// Legacy bridge
|
|
808
|
+
isRemoteUpdate() {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
// Serialize state
|
|
812
|
+
serialize() {
|
|
813
|
+
const json = {};
|
|
814
|
+
for (const [key, val] of this.rootMap) {
|
|
815
|
+
const entryMap = val;
|
|
816
|
+
const attrs = entryMap.get("attributes");
|
|
817
|
+
let value = entryMap.get("value");
|
|
818
|
+
if (attrs?.type === "document" && value instanceof Y__namespace.Text) {
|
|
819
|
+
value = value.toString();
|
|
820
|
+
}
|
|
821
|
+
json[key] = {
|
|
822
|
+
value,
|
|
823
|
+
attributes: attrs
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
return json;
|
|
827
|
+
}
|
|
828
|
+
// Deserialize state (for IndexedDBAdapter compatibility)
|
|
829
|
+
deserialize(data) {
|
|
830
|
+
this.doc.transact(() => {
|
|
831
|
+
for (const [key, entry] of Object.entries(data)) {
|
|
832
|
+
if (key.startsWith("$")) {
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
let entryMap = this.rootMap.get(key);
|
|
836
|
+
if (!entryMap) {
|
|
837
|
+
entryMap = new Y__namespace.Map();
|
|
838
|
+
this.rootMap.set(key, entryMap);
|
|
839
|
+
}
|
|
840
|
+
entryMap.set("value", entry.value);
|
|
841
|
+
entryMap.set("attributes", entry.attributes);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
751
844
|
}
|
|
752
|
-
// Helper method to encode file to base64
|
|
753
845
|
encodeFileToBase64(file) {
|
|
754
846
|
return new Promise((resolve, reject) => {
|
|
755
847
|
if (typeof FileReader !== "undefined") {
|
|
@@ -766,11 +858,9 @@ var MindCache = class {
|
|
|
766
858
|
}
|
|
767
859
|
});
|
|
768
860
|
}
|
|
769
|
-
// Helper method to create data URL from base64 and content type
|
|
770
861
|
createDataUrl(base64Data, contentType) {
|
|
771
862
|
return `data:${contentType};base64,${base64Data}`;
|
|
772
863
|
}
|
|
773
|
-
// Helper method to validate content type for different STM types
|
|
774
864
|
validateContentType(type, contentType) {
|
|
775
865
|
if (type === "text" || type === "json") {
|
|
776
866
|
return true;
|
|
@@ -786,11 +876,17 @@ var MindCache = class {
|
|
|
786
876
|
}
|
|
787
877
|
return false;
|
|
788
878
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
return
|
|
879
|
+
// InjectSTM replacement
|
|
880
|
+
injectSTM(template, _processingStack) {
|
|
881
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
|
|
882
|
+
const val = this.get_value(key.trim(), _processingStack);
|
|
883
|
+
return val !== void 0 ? String(val) : `{{${key}}}`;
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
// Public API Methods
|
|
887
|
+
getAll() {
|
|
888
|
+
return this.serialize();
|
|
792
889
|
}
|
|
793
|
-
// Get a value from the STM with template processing if enabled
|
|
794
890
|
get_value(key, _processingStack) {
|
|
795
891
|
if (key === "$date") {
|
|
796
892
|
const today = /* @__PURE__ */ new Date();
|
|
@@ -800,32 +896,37 @@ var MindCache = class {
|
|
|
800
896
|
const now = /* @__PURE__ */ new Date();
|
|
801
897
|
return now.toTimeString().split(" ")[0];
|
|
802
898
|
}
|
|
803
|
-
|
|
804
|
-
|
|
899
|
+
if (key === "$version") {
|
|
900
|
+
return this.version;
|
|
901
|
+
}
|
|
902
|
+
const entryMap = this.rootMap.get(key);
|
|
903
|
+
if (!entryMap) {
|
|
805
904
|
return void 0;
|
|
806
905
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
906
|
+
const attributes = entryMap.get("attributes");
|
|
907
|
+
const value = entryMap.get("value");
|
|
908
|
+
if (attributes?.type === "document" && value instanceof Y__namespace.Text) {
|
|
909
|
+
return value.toString();
|
|
910
|
+
}
|
|
911
|
+
if (_processingStack && _processingStack.has(key)) {
|
|
912
|
+
return `{{${key}}}`;
|
|
913
|
+
}
|
|
914
|
+
if (attributes?.systemTags?.includes("ApplyTemplate") || attributes?.systemTags?.includes("template") || attributes?.template) {
|
|
915
|
+
if (typeof value === "string") {
|
|
916
|
+
const stack = _processingStack || /* @__PURE__ */ new Set();
|
|
917
|
+
stack.add(key);
|
|
918
|
+
return this.injectSTM(value, stack);
|
|
811
919
|
}
|
|
812
|
-
processingStack.add(key);
|
|
813
|
-
const result = this.injectSTM(entry.value, processingStack);
|
|
814
|
-
processingStack.delete(key);
|
|
815
|
-
return result;
|
|
816
920
|
}
|
|
817
|
-
return
|
|
921
|
+
return value;
|
|
818
922
|
}
|
|
819
|
-
// Get attributes for a key
|
|
820
923
|
get_attributes(key) {
|
|
821
|
-
if (key === "$date" || key === "$time") {
|
|
924
|
+
if (key === "$date" || key === "$time" || key === "$version") {
|
|
822
925
|
return {
|
|
823
926
|
type: "text",
|
|
824
927
|
contentTags: [],
|
|
825
928
|
systemTags: ["prompt", "readonly", "protected"],
|
|
826
929
|
zIndex: 999999,
|
|
827
|
-
// System keys appear last
|
|
828
|
-
// Legacy attributes
|
|
829
930
|
readonly: true,
|
|
830
931
|
visible: true,
|
|
831
932
|
hardcoded: true,
|
|
@@ -833,1270 +934,486 @@ var MindCache = class {
|
|
|
833
934
|
tags: []
|
|
834
935
|
};
|
|
835
936
|
}
|
|
836
|
-
const
|
|
837
|
-
return
|
|
937
|
+
const entryMap = this.rootMap.get(key);
|
|
938
|
+
return entryMap ? entryMap.get("attributes") : void 0;
|
|
838
939
|
}
|
|
839
|
-
// Set a value in the STM with default attributes
|
|
840
940
|
set_value(key, value, attributes) {
|
|
841
|
-
if (key === "$date" || key === "$time") {
|
|
941
|
+
if (key === "$date" || key === "$time" || key === "$version") {
|
|
842
942
|
return;
|
|
843
943
|
}
|
|
844
|
-
const existingEntry = this.
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
tags: [...existingEntry.attributes.tags || []],
|
|
851
|
-
zIndex: existingEntry.attributes.zIndex ?? 0
|
|
852
|
-
} : {
|
|
853
|
-
...DEFAULT_KEY_ATTRIBUTES,
|
|
854
|
-
contentTags: [],
|
|
855
|
-
// Fresh array
|
|
856
|
-
systemTags: ["SystemPrompt", "LLMWrite"],
|
|
857
|
-
// Fresh array with default
|
|
858
|
-
tags: [],
|
|
859
|
-
// Fresh array
|
|
860
|
-
zIndex: 0
|
|
861
|
-
};
|
|
862
|
-
const finalAttributes = attributes ? { ...baseAttributes, ...attributes } : baseAttributes;
|
|
863
|
-
let systemTags = this.normalizeSystemTags(finalAttributes.systemTags || []);
|
|
864
|
-
if (attributes) {
|
|
865
|
-
if ("readonly" in attributes) {
|
|
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");
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
if ("visible" in attributes) {
|
|
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");
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
if ("systemTags" in attributes && Array.isArray(attributes.systemTags)) {
|
|
889
|
-
systemTags = this.normalizeSystemTags(attributes.systemTags);
|
|
890
|
-
}
|
|
891
|
-
if ("hardcoded" in attributes) {
|
|
892
|
-
if (attributes.hardcoded && !systemTags.includes("protected")) {
|
|
893
|
-
systemTags.push("protected");
|
|
894
|
-
} else if (!attributes.hardcoded && !wasHardcoded) {
|
|
895
|
-
systemTags = systemTags.filter((t) => t !== "protected");
|
|
896
|
-
}
|
|
897
|
-
if (wasHardcoded && !systemTags.includes("protected")) {
|
|
898
|
-
systemTags.push("protected");
|
|
899
|
-
}
|
|
900
|
-
} else if (wasHardcoded) {
|
|
901
|
-
if (!systemTags.includes("protected")) {
|
|
902
|
-
systemTags.push("protected");
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
if ("template" in attributes) {
|
|
906
|
-
if (attributes.template && !wasHardcoded && !systemTags.includes("ApplyTemplate") && !systemTags.includes("template")) {
|
|
907
|
-
systemTags.push("ApplyTemplate");
|
|
908
|
-
} else if (!attributes.template || wasHardcoded) {
|
|
909
|
-
systemTags = systemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
|
|
944
|
+
const existingEntry = this.rootMap.get(key);
|
|
945
|
+
if (existingEntry) {
|
|
946
|
+
const existingAttrs = existingEntry.get("attributes");
|
|
947
|
+
if (existingAttrs?.type === "document") {
|
|
948
|
+
if (typeof value === "string") {
|
|
949
|
+
this.replace_document_text(key, value);
|
|
910
950
|
}
|
|
951
|
+
return;
|
|
911
952
|
}
|
|
912
|
-
} else if (wasHardcoded) {
|
|
913
|
-
if (!systemTags.includes("protected")) {
|
|
914
|
-
systemTags.push("protected");
|
|
915
|
-
}
|
|
916
|
-
systemTags = systemTags.filter((t) => t !== "LLMWrite");
|
|
917
|
-
if (!systemTags.includes("readonly")) {
|
|
918
|
-
systemTags.push("readonly");
|
|
919
|
-
}
|
|
920
|
-
systemTags = systemTags.filter((t) => t !== "template");
|
|
921
953
|
}
|
|
922
|
-
|
|
923
|
-
|
|
954
|
+
let entryMap = this.rootMap.get(key);
|
|
955
|
+
const isNewEntry = !entryMap;
|
|
956
|
+
if (isNewEntry) {
|
|
957
|
+
entryMap = new Y__namespace.Map();
|
|
958
|
+
this.rootMap.set(key, entryMap);
|
|
924
959
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
960
|
+
this.getUndoManager(key);
|
|
961
|
+
this.doc.transact(() => {
|
|
962
|
+
const oldAttributes = isNewEntry ? {
|
|
963
|
+
...DEFAULT_KEY_ATTRIBUTES,
|
|
964
|
+
contentTags: [],
|
|
965
|
+
systemTags: ["SystemPrompt", "LLMWrite"],
|
|
966
|
+
tags: [],
|
|
967
|
+
zIndex: 0
|
|
968
|
+
} : entryMap.get("attributes");
|
|
969
|
+
const finalAttributes = attributes ? { ...oldAttributes, ...attributes } : oldAttributes;
|
|
970
|
+
let normalizedAttributes = { ...finalAttributes };
|
|
971
|
+
if (finalAttributes.systemTags) {
|
|
972
|
+
normalizedAttributes.systemTags = this.normalizeSystemTags(finalAttributes.systemTags);
|
|
973
|
+
}
|
|
974
|
+
if (finalAttributes.template) {
|
|
975
|
+
if (!normalizedAttributes.systemTags.includes("template")) {
|
|
976
|
+
normalizedAttributes.systemTags.push("template");
|
|
977
|
+
}
|
|
929
978
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
finalAttributes.readonly = systemTags.includes("readonly") || !systemTags.includes("LLMWrite");
|
|
934
|
-
finalAttributes.visible = this.hasSystemPrompt(systemTags);
|
|
935
|
-
finalAttributes.hardcoded = wasHardcoded || systemTags.includes("protected");
|
|
936
|
-
finalAttributes.template = systemTags.includes("ApplyTemplate") || systemTags.includes("template");
|
|
937
|
-
if (attributes && "tags" in attributes && attributes.tags) {
|
|
938
|
-
finalAttributes.contentTags = [...attributes.tags];
|
|
939
|
-
}
|
|
940
|
-
finalAttributes.tags = [...finalAttributes.contentTags || []];
|
|
941
|
-
this.stm[key] = {
|
|
942
|
-
value,
|
|
943
|
-
attributes: finalAttributes
|
|
944
|
-
};
|
|
945
|
-
if (this.listeners[key]) {
|
|
946
|
-
this.listeners[key].forEach((listener) => listener(value));
|
|
947
|
-
}
|
|
948
|
-
this.notifyGlobalListeners();
|
|
979
|
+
entryMap.set("value", value);
|
|
980
|
+
entryMap.set("attributes", normalizedAttributes);
|
|
981
|
+
});
|
|
949
982
|
}
|
|
950
|
-
|
|
951
|
-
// This doesn't trigger the global listener to prevent sync loops
|
|
952
|
-
_setFromRemote(key, value, attributes) {
|
|
983
|
+
delete_key(key) {
|
|
953
984
|
if (key === "$date" || key === "$time") {
|
|
954
985
|
return;
|
|
955
986
|
}
|
|
956
|
-
this.
|
|
957
|
-
let systemTags = attributes.systemTags || [];
|
|
958
|
-
if (!attributes.systemTags || systemTags.length === 0) {
|
|
959
|
-
systemTags = [];
|
|
960
|
-
if (attributes.visible !== false) {
|
|
961
|
-
systemTags.push("prompt");
|
|
962
|
-
}
|
|
963
|
-
if (attributes.readonly) {
|
|
964
|
-
systemTags.push("readonly");
|
|
965
|
-
} else {
|
|
966
|
-
systemTags.push("LLMWrite");
|
|
967
|
-
}
|
|
968
|
-
if (attributes.hardcoded) {
|
|
969
|
-
systemTags.push("protected");
|
|
970
|
-
}
|
|
971
|
-
if (attributes.template) {
|
|
972
|
-
systemTags.push("ApplyTemplate");
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
systemTags = this.normalizeSystemTags(systemTags);
|
|
976
|
-
const contentTags = attributes.contentTags || attributes.tags || [];
|
|
977
|
-
this.stm[key] = {
|
|
978
|
-
value,
|
|
979
|
-
attributes: {
|
|
980
|
-
...attributes,
|
|
981
|
-
contentTags,
|
|
982
|
-
systemTags,
|
|
983
|
-
zIndex: attributes.zIndex ?? 0,
|
|
984
|
-
tags: contentTags,
|
|
985
|
-
// Sync legacy attributes FROM normalized systemTags
|
|
986
|
-
readonly: systemTags.includes("readonly") || !systemTags.includes("LLMWrite"),
|
|
987
|
-
visible: this.hasSystemPrompt(systemTags),
|
|
988
|
-
hardcoded: systemTags.includes("protected"),
|
|
989
|
-
template: systemTags.includes("ApplyTemplate") || systemTags.includes("template")
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
if (this.listeners[key]) {
|
|
993
|
-
this.listeners[key].forEach((listener) => listener(value));
|
|
994
|
-
}
|
|
995
|
-
this.notifyGlobalListeners();
|
|
996
|
-
this._isRemoteUpdate = false;
|
|
987
|
+
this.rootMap.delete(key);
|
|
997
988
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
989
|
+
clear() {
|
|
990
|
+
const keys = Array.from(this.rootMap.keys());
|
|
991
|
+
this.doc.transact(() => {
|
|
992
|
+
keys.forEach((k) => this.rootMap.delete(k));
|
|
993
|
+
});
|
|
1001
994
|
}
|
|
1002
|
-
//
|
|
1003
|
-
|
|
1004
|
-
|
|
995
|
+
// File methods
|
|
996
|
+
async set_file(key, file, attributes) {
|
|
997
|
+
const base64 = await this.encodeFileToBase64(file);
|
|
998
|
+
const dataUrl = this.createDataUrl(base64, file.type);
|
|
999
|
+
this.set_value(key, dataUrl, {
|
|
1000
|
+
...attributes,
|
|
1001
|
+
type: "file",
|
|
1002
|
+
contentType: file.type
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
set_image(key, file, attributes) {
|
|
1006
|
+
return this.set_file(key, file, {
|
|
1007
|
+
...attributes,
|
|
1008
|
+
type: "image"
|
|
1009
|
+
// Override to image
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
// Document methods for collaborative editing
|
|
1013
|
+
/**
|
|
1014
|
+
* Create or get a collaborative document key.
|
|
1015
|
+
* Uses Y.Text for character-level concurrent editing.
|
|
1016
|
+
*
|
|
1017
|
+
* Note: This exposes Yjs Y.Text directly for editor bindings (y-quill, y-codemirror, etc.)
|
|
1018
|
+
*/
|
|
1019
|
+
set_document(key, initialText, attributes) {
|
|
1020
|
+
if (key === "$date" || key === "$time" || key === "$version") {
|
|
1005
1021
|
return;
|
|
1006
1022
|
}
|
|
1007
|
-
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1023
|
+
let entryMap = this.rootMap.get(key);
|
|
1024
|
+
if (!entryMap) {
|
|
1025
|
+
entryMap = new Y__namespace.Map();
|
|
1026
|
+
this.rootMap.set(key, entryMap);
|
|
1027
|
+
const yText = new Y__namespace.Text(initialText || "");
|
|
1028
|
+
entryMap.set("value", yText);
|
|
1029
|
+
entryMap.set("attributes", {
|
|
1030
|
+
...DEFAULT_KEY_ATTRIBUTES,
|
|
1031
|
+
type: "document",
|
|
1032
|
+
contentTags: [],
|
|
1033
|
+
systemTags: ["SystemPrompt", "LLMWrite"],
|
|
1034
|
+
tags: [],
|
|
1035
|
+
zIndex: 0,
|
|
1036
|
+
...attributes
|
|
1037
|
+
});
|
|
1014
1038
|
}
|
|
1015
|
-
this.
|
|
1039
|
+
this.getUndoManager(key);
|
|
1016
1040
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1041
|
+
/**
|
|
1042
|
+
* Get the Y.Text object for a document key.
|
|
1043
|
+
* Use this to bind to editors (Quill, CodeMirror, Monaco, etc.)
|
|
1044
|
+
*
|
|
1045
|
+
* @returns Y.Text or undefined if key doesn't exist or isn't a document
|
|
1046
|
+
*/
|
|
1047
|
+
get_document(key) {
|
|
1048
|
+
const entryMap = this.rootMap.get(key);
|
|
1049
|
+
if (!entryMap) {
|
|
1050
|
+
return void 0;
|
|
1051
|
+
}
|
|
1052
|
+
const attrs = entryMap.get("attributes");
|
|
1053
|
+
if (attrs?.type !== "document") {
|
|
1054
|
+
return void 0;
|
|
1055
|
+
}
|
|
1056
|
+
const value = entryMap.get("value");
|
|
1057
|
+
if (value instanceof Y__namespace.Text) {
|
|
1058
|
+
return value;
|
|
1059
|
+
}
|
|
1060
|
+
return void 0;
|
|
1023
1061
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1062
|
+
/**
|
|
1063
|
+
* Get plain text content of a document key.
|
|
1064
|
+
* For collaborative editing, use get_document() and bind to an editor.
|
|
1065
|
+
*/
|
|
1066
|
+
get_document_text(key) {
|
|
1067
|
+
const yText = this.get_document(key);
|
|
1068
|
+
return yText?.toString();
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Insert text at a position in a document key.
|
|
1072
|
+
*/
|
|
1073
|
+
insert_text(key, index, text) {
|
|
1074
|
+
const yText = this.get_document(key);
|
|
1075
|
+
if (yText) {
|
|
1076
|
+
yText.insert(index, text);
|
|
1028
1077
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Delete text from a document key.
|
|
1081
|
+
*/
|
|
1082
|
+
delete_text(key, index, length2) {
|
|
1083
|
+
const yText = this.get_document(key);
|
|
1084
|
+
if (yText) {
|
|
1085
|
+
yText.delete(index, length2);
|
|
1032
1086
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Replace all text in a document key.
|
|
1090
|
+
* Uses diff-based updates when changes are < diffThreshold (default 80%).
|
|
1091
|
+
* This preserves concurrent edits and provides better undo granularity.
|
|
1092
|
+
*
|
|
1093
|
+
* @param key - The document key
|
|
1094
|
+
* @param newText - The new text content
|
|
1095
|
+
* @param diffThreshold - Percentage (0-1) of change above which full replace is used (default: 0.8)
|
|
1096
|
+
*/
|
|
1097
|
+
replace_document_text(key, newText, diffThreshold = 0.8) {
|
|
1098
|
+
const yText = this.get_document(key);
|
|
1099
|
+
if (!yText) {
|
|
1100
|
+
return;
|
|
1042
1101
|
}
|
|
1043
|
-
|
|
1044
|
-
if (
|
|
1045
|
-
|
|
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);
|
|
1065
|
-
}
|
|
1066
|
-
if (newSystemTags.includes("protected")) {
|
|
1067
|
-
newSystemTags = newSystemTags.filter((t) => t !== "LLMWrite");
|
|
1068
|
-
if (!newSystemTags.includes("readonly")) {
|
|
1069
|
-
newSystemTags.push("readonly");
|
|
1070
|
-
}
|
|
1071
|
-
newSystemTags = newSystemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
|
|
1072
|
-
entry.attributes.readonly = true;
|
|
1073
|
-
entry.attributes.template = false;
|
|
1074
|
-
}
|
|
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");
|
|
1079
|
-
} else if (wasHardcoded) {
|
|
1080
|
-
let systemTags = this.normalizeSystemTags(entry.attributes.systemTags || []);
|
|
1081
|
-
if (!systemTags.includes("protected")) {
|
|
1082
|
-
systemTags.push("protected");
|
|
1083
|
-
}
|
|
1084
|
-
systemTags = systemTags.filter((t) => t !== "LLMWrite");
|
|
1085
|
-
if (!systemTags.includes("readonly")) {
|
|
1086
|
-
systemTags.push("readonly");
|
|
1087
|
-
}
|
|
1088
|
-
systemTags = systemTags.filter((t) => t !== "ApplyTemplate" && t !== "template");
|
|
1089
|
-
entry.attributes.systemTags = systemTags;
|
|
1090
|
-
entry.attributes.readonly = true;
|
|
1091
|
-
entry.attributes.visible = this.hasSystemPrompt(systemTags);
|
|
1092
|
-
entry.attributes.template = false;
|
|
1093
|
-
}
|
|
1094
|
-
if (wasHardcoded) {
|
|
1095
|
-
entry.attributes.hardcoded = true;
|
|
1096
|
-
if (!entry.attributes.systemTags?.includes("protected")) {
|
|
1097
|
-
entry.attributes.systemTags = [...entry.attributes.systemTags || [], "protected"];
|
|
1098
|
-
}
|
|
1099
|
-
entry.attributes.readonly = true;
|
|
1100
|
-
entry.attributes.template = false;
|
|
1101
|
-
}
|
|
1102
|
-
if ("contentTags" in attributes) {
|
|
1103
|
-
entry.attributes.tags = [...entry.attributes.contentTags || []];
|
|
1104
|
-
}
|
|
1105
|
-
this.notifyGlobalListeners();
|
|
1106
|
-
return true;
|
|
1107
|
-
}
|
|
1108
|
-
set(key, value) {
|
|
1109
|
-
this.set_value(key, value);
|
|
1110
|
-
}
|
|
1111
|
-
async set_file(key, file, attributes) {
|
|
1112
|
-
const base64Data = await this.encodeFileToBase64(file);
|
|
1113
|
-
const contentType = file.type;
|
|
1114
|
-
const fileAttributes = {
|
|
1115
|
-
type: contentType.startsWith("image/") ? "image" : "file",
|
|
1116
|
-
contentType,
|
|
1117
|
-
...attributes
|
|
1118
|
-
};
|
|
1119
|
-
this.set_value(key, base64Data, fileAttributes);
|
|
1120
|
-
}
|
|
1121
|
-
set_base64(key, base64Data, contentType, type = "file", attributes) {
|
|
1122
|
-
if (!this.validateContentType(type, contentType)) {
|
|
1123
|
-
throw new Error(`Invalid content type ${contentType} for type ${type}`);
|
|
1124
|
-
}
|
|
1125
|
-
const fileAttributes = {
|
|
1126
|
-
type,
|
|
1127
|
-
contentType,
|
|
1128
|
-
...attributes
|
|
1129
|
-
};
|
|
1130
|
-
this.set_value(key, base64Data, fileAttributes);
|
|
1131
|
-
}
|
|
1132
|
-
add_image(key, base64Data, contentType = "image/jpeg", attributes) {
|
|
1133
|
-
if (!contentType.startsWith("image/")) {
|
|
1134
|
-
throw new Error(`Invalid image content type: ${contentType}. Must start with 'image/'`);
|
|
1135
|
-
}
|
|
1136
|
-
this.set_base64(key, base64Data, contentType, "image", attributes);
|
|
1137
|
-
this.set_attributes(key, {
|
|
1138
|
-
type: "image",
|
|
1139
|
-
contentType
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
get_data_url(key) {
|
|
1143
|
-
const entry = this.stm[key];
|
|
1144
|
-
if (!entry || entry.attributes.type !== "image" && entry.attributes.type !== "file") {
|
|
1145
|
-
return void 0;
|
|
1146
|
-
}
|
|
1147
|
-
if (!entry.attributes.contentType) {
|
|
1148
|
-
return void 0;
|
|
1149
|
-
}
|
|
1150
|
-
return this.createDataUrl(entry.value, entry.attributes.contentType);
|
|
1151
|
-
}
|
|
1152
|
-
get_base64(key) {
|
|
1153
|
-
const entry = this.stm[key];
|
|
1154
|
-
if (!entry || entry.attributes.type !== "image" && entry.attributes.type !== "file") {
|
|
1155
|
-
return void 0;
|
|
1156
|
-
}
|
|
1157
|
-
return entry.value;
|
|
1158
|
-
}
|
|
1159
|
-
has(key) {
|
|
1160
|
-
if (key === "$date" || key === "$time") {
|
|
1161
|
-
return true;
|
|
1162
|
-
}
|
|
1163
|
-
return key in this.stm;
|
|
1164
|
-
}
|
|
1165
|
-
delete(key) {
|
|
1166
|
-
if (key === "$date" || key === "$time") {
|
|
1167
|
-
return false;
|
|
1102
|
+
const oldText = yText.toString();
|
|
1103
|
+
if (oldText === newText) {
|
|
1104
|
+
return;
|
|
1168
1105
|
}
|
|
1169
|
-
if (
|
|
1170
|
-
|
|
1106
|
+
if (oldText.length === 0) {
|
|
1107
|
+
yText.insert(0, newText);
|
|
1108
|
+
return;
|
|
1171
1109
|
}
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
if (
|
|
1176
|
-
|
|
1110
|
+
const diffs = diff__default.default(oldText, newText);
|
|
1111
|
+
let changedChars = 0;
|
|
1112
|
+
for (const [op, text] of diffs) {
|
|
1113
|
+
if (op !== 0) {
|
|
1114
|
+
changedChars += text.length;
|
|
1177
1115
|
}
|
|
1178
1116
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
}
|
|
1198
|
-
keys() {
|
|
1199
|
-
return [...this.getSortedKeys(), "$date", "$time"];
|
|
1200
|
-
}
|
|
1201
|
-
values() {
|
|
1202
|
-
const now = /* @__PURE__ */ new Date();
|
|
1203
|
-
const sortedKeys = this.getSortedKeys();
|
|
1204
|
-
const stmValues = sortedKeys.map((key) => this.stm[key].value);
|
|
1205
|
-
return [
|
|
1206
|
-
...stmValues,
|
|
1207
|
-
now.toISOString().split("T")[0],
|
|
1208
|
-
now.toTimeString().split(" ")[0]
|
|
1209
|
-
];
|
|
1210
|
-
}
|
|
1211
|
-
entries() {
|
|
1212
|
-
const now = /* @__PURE__ */ new Date();
|
|
1213
|
-
const sortedKeys = this.getSortedKeys();
|
|
1214
|
-
const stmEntries = sortedKeys.map(
|
|
1215
|
-
(key) => [key, this.stm[key].value]
|
|
1216
|
-
);
|
|
1217
|
-
return [
|
|
1218
|
-
...stmEntries,
|
|
1219
|
-
["$date", now.toISOString().split("T")[0]],
|
|
1220
|
-
["$time", now.toTimeString().split(" ")[0]]
|
|
1221
|
-
];
|
|
1222
|
-
}
|
|
1223
|
-
size() {
|
|
1224
|
-
return Object.keys(this.stm).length + 2;
|
|
1225
|
-
}
|
|
1226
|
-
getAll() {
|
|
1227
|
-
const now = /* @__PURE__ */ new Date();
|
|
1228
|
-
const result = {};
|
|
1229
|
-
const sortedKeys = this.getSortedKeys();
|
|
1230
|
-
sortedKeys.forEach((key) => {
|
|
1231
|
-
result[key] = this.stm[key].value;
|
|
1232
|
-
});
|
|
1233
|
-
result["$date"] = now.toISOString().split("T")[0];
|
|
1234
|
-
result["$time"] = now.toTimeString().split(" ")[0];
|
|
1235
|
-
return result;
|
|
1236
|
-
}
|
|
1237
|
-
update(newValues) {
|
|
1238
|
-
Object.entries(newValues).forEach(([key, value]) => {
|
|
1239
|
-
if (key !== "$date" && key !== "$time") {
|
|
1240
|
-
this.stm[key] = {
|
|
1241
|
-
value,
|
|
1242
|
-
attributes: { ...DEFAULT_KEY_ATTRIBUTES }
|
|
1243
|
-
};
|
|
1244
|
-
if (this.listeners[key]) {
|
|
1245
|
-
this.listeners[key].forEach((listener) => listener(this.stm[key]?.value));
|
|
1117
|
+
const changeRatio = changedChars / Math.max(oldText.length, newText.length);
|
|
1118
|
+
if (changeRatio > diffThreshold) {
|
|
1119
|
+
this.doc.transact(() => {
|
|
1120
|
+
yText.delete(0, yText.length);
|
|
1121
|
+
yText.insert(0, newText);
|
|
1122
|
+
});
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
this.doc.transact(() => {
|
|
1126
|
+
let cursor = 0;
|
|
1127
|
+
for (const [op, text] of diffs) {
|
|
1128
|
+
if (op === 0) {
|
|
1129
|
+
cursor += text.length;
|
|
1130
|
+
} else if (op === -1) {
|
|
1131
|
+
yText.delete(cursor, text.length);
|
|
1132
|
+
} else if (op === 1) {
|
|
1133
|
+
yText.insert(cursor, text);
|
|
1134
|
+
cursor += text.length;
|
|
1246
1135
|
}
|
|
1247
1136
|
}
|
|
1248
1137
|
});
|
|
1249
|
-
this.notifyGlobalListeners();
|
|
1250
1138
|
}
|
|
1139
|
+
// ... (subscribe methods)
|
|
1251
1140
|
subscribe(key, listener) {
|
|
1252
1141
|
if (!this.listeners[key]) {
|
|
1253
1142
|
this.listeners[key] = [];
|
|
1254
1143
|
}
|
|
1255
1144
|
this.listeners[key].push(listener);
|
|
1256
|
-
|
|
1257
|
-
unsubscribe(key, listener) {
|
|
1258
|
-
if (this.listeners[key]) {
|
|
1145
|
+
return () => {
|
|
1259
1146
|
this.listeners[key] = this.listeners[key].filter((l) => l !== listener);
|
|
1260
|
-
}
|
|
1147
|
+
};
|
|
1261
1148
|
}
|
|
1262
1149
|
subscribeToAll(listener) {
|
|
1263
1150
|
this.globalListeners.push(listener);
|
|
1151
|
+
return () => {
|
|
1152
|
+
this.globalListeners = this.globalListeners.filter((l) => l !== listener);
|
|
1153
|
+
};
|
|
1264
1154
|
}
|
|
1265
1155
|
unsubscribeFromAll(listener) {
|
|
1266
1156
|
this.globalListeners = this.globalListeners.filter((l) => l !== listener);
|
|
1267
1157
|
}
|
|
1268
1158
|
notifyGlobalListeners() {
|
|
1269
|
-
this.globalListeners.forEach((
|
|
1270
|
-
}
|
|
1271
|
-
injectSTM(template, _processingStack) {
|
|
1272
|
-
if (template === null || template === void 0) {
|
|
1273
|
-
return String(template);
|
|
1274
|
-
}
|
|
1275
|
-
const templateStr = String(template);
|
|
1276
|
-
const keys = templateStr.match(/\{\{([$\w]+)\}\}/g);
|
|
1277
|
-
if (!keys) {
|
|
1278
|
-
return templateStr;
|
|
1279
|
-
}
|
|
1280
|
-
const cleanKeys = keys.map((key) => key.replace(/[{}]/g, ""));
|
|
1281
|
-
const inputValues = cleanKeys.reduce((acc, key) => {
|
|
1282
|
-
if (key === "$date" || key === "$time") {
|
|
1283
|
-
return {
|
|
1284
|
-
...acc,
|
|
1285
|
-
[key]: this.get_value(key, _processingStack)
|
|
1286
|
-
};
|
|
1287
|
-
}
|
|
1288
|
-
const attributes = this.get_attributes(key);
|
|
1289
|
-
if (_processingStack || attributes && attributes.visible) {
|
|
1290
|
-
if (attributes && (attributes.type === "image" || attributes.type === "file")) {
|
|
1291
|
-
return acc;
|
|
1292
|
-
}
|
|
1293
|
-
return {
|
|
1294
|
-
...acc,
|
|
1295
|
-
[key]: this.get_value(key, _processingStack)
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
return acc;
|
|
1299
|
-
}, {});
|
|
1300
|
-
return templateStr.replace(/\{\{([$\w]+)\}\}/g, (match, key) => {
|
|
1301
|
-
if (inputValues[key] !== void 0) {
|
|
1302
|
-
return inputValues[key];
|
|
1303
|
-
}
|
|
1304
|
-
const attributes = this.get_attributes(key);
|
|
1305
|
-
if (attributes && (attributes.type === "image" || attributes.type === "file")) {
|
|
1306
|
-
return match;
|
|
1307
|
-
}
|
|
1308
|
-
return "";
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
getSTM() {
|
|
1312
|
-
const now = /* @__PURE__ */ new Date();
|
|
1313
|
-
const entries = [];
|
|
1314
|
-
const sortedKeys = this.getSortedKeys();
|
|
1315
|
-
sortedKeys.forEach((key) => {
|
|
1316
|
-
const entry = this.stm[key];
|
|
1317
|
-
if (entry.attributes.visible) {
|
|
1318
|
-
entries.push([key, this.get_value(key)]);
|
|
1319
|
-
}
|
|
1320
|
-
});
|
|
1321
|
-
entries.push(["$date", now.toISOString().split("T")[0]]);
|
|
1322
|
-
entries.push(["$time", now.toTimeString().split(" ")[0]]);
|
|
1323
|
-
return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
|
|
1324
|
-
}
|
|
1325
|
-
getSTMObject() {
|
|
1326
|
-
return this.getAll();
|
|
1327
|
-
}
|
|
1328
|
-
getSTMForAPI() {
|
|
1329
|
-
const now = /* @__PURE__ */ new Date();
|
|
1330
|
-
const apiData = [];
|
|
1331
|
-
const sortedKeys = this.getSortedKeys();
|
|
1332
|
-
sortedKeys.forEach((key) => {
|
|
1333
|
-
const entry = this.stm[key];
|
|
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;
|
|
1337
|
-
apiData.push({
|
|
1338
|
-
key,
|
|
1339
|
-
value: processedValue,
|
|
1340
|
-
type: entry.attributes.type,
|
|
1341
|
-
contentType: entry.attributes.contentType
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
});
|
|
1345
|
-
apiData.push({
|
|
1346
|
-
key: "$date",
|
|
1347
|
-
value: now.toISOString().split("T")[0],
|
|
1348
|
-
type: "text"
|
|
1349
|
-
});
|
|
1350
|
-
apiData.push({
|
|
1351
|
-
key: "$time",
|
|
1352
|
-
value: now.toTimeString().split(" ")[0],
|
|
1353
|
-
type: "text"
|
|
1354
|
-
});
|
|
1355
|
-
return apiData;
|
|
1356
|
-
}
|
|
1357
|
-
getVisibleImages() {
|
|
1358
|
-
const imageParts = [];
|
|
1359
|
-
const sortedKeys = this.getSortedKeys();
|
|
1360
|
-
sortedKeys.forEach((key) => {
|
|
1361
|
-
const entry = this.stm[key];
|
|
1362
|
-
if ((this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) && entry.attributes.type === "image" && entry.attributes.contentType) {
|
|
1363
|
-
const dataUrl = this.createDataUrl(entry.value, entry.attributes.contentType);
|
|
1364
|
-
imageParts.push({
|
|
1365
|
-
type: "file",
|
|
1366
|
-
mediaType: entry.attributes.contentType,
|
|
1367
|
-
url: dataUrl,
|
|
1368
|
-
filename: key
|
|
1369
|
-
});
|
|
1370
|
-
}
|
|
1371
|
-
});
|
|
1372
|
-
return imageParts;
|
|
1159
|
+
this.globalListeners.forEach((l) => l());
|
|
1373
1160
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1161
|
+
// Sanitize key name for use in tool names
|
|
1162
|
+
sanitizeKeyForTool(key) {
|
|
1163
|
+
return key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1376
1164
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
this.
|
|
1381
|
-
|
|
1382
|
-
console.error("MindCache: Failed to deserialize JSON:", error);
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
serialize() {
|
|
1386
|
-
const result = {};
|
|
1387
|
-
const sortedKeys = this.getSortedKeys();
|
|
1388
|
-
sortedKeys.forEach((key) => {
|
|
1389
|
-
const entry = this.stm[key];
|
|
1390
|
-
if (!entry.attributes.hardcoded) {
|
|
1391
|
-
result[key] = {
|
|
1392
|
-
value: entry.value,
|
|
1393
|
-
attributes: { ...entry.attributes }
|
|
1394
|
-
};
|
|
1165
|
+
// Find original key from sanitized tool name
|
|
1166
|
+
findKeyFromSanitizedTool(sanitizedKey) {
|
|
1167
|
+
for (const [key] of this.rootMap) {
|
|
1168
|
+
if (this.sanitizeKeyForTool(key) === sanitizedKey) {
|
|
1169
|
+
return key;
|
|
1395
1170
|
}
|
|
1396
|
-
});
|
|
1397
|
-
return result;
|
|
1398
|
-
}
|
|
1399
|
-
deserialize(data) {
|
|
1400
|
-
if (typeof data === "object" && data !== null) {
|
|
1401
|
-
this._isRemoteUpdate = true;
|
|
1402
|
-
this.clear();
|
|
1403
|
-
Object.entries(data).forEach(([key, entry]) => {
|
|
1404
|
-
if (entry && typeof entry === "object" && "value" in entry && "attributes" in entry) {
|
|
1405
|
-
const attrs = entry.attributes;
|
|
1406
|
-
if (attrs.hardcoded === true || attrs.systemTags?.includes("protected")) {
|
|
1407
|
-
return;
|
|
1408
|
-
}
|
|
1409
|
-
let systemTags = attrs.systemTags || [];
|
|
1410
|
-
if (!attrs.systemTags || systemTags.length === 0) {
|
|
1411
|
-
systemTags = [];
|
|
1412
|
-
if (attrs.visible !== false) {
|
|
1413
|
-
systemTags.push("prompt");
|
|
1414
|
-
}
|
|
1415
|
-
if (attrs.readonly) {
|
|
1416
|
-
systemTags.push("readonly");
|
|
1417
|
-
} else {
|
|
1418
|
-
systemTags.push("LLMWrite");
|
|
1419
|
-
}
|
|
1420
|
-
if (attrs.hardcoded) {
|
|
1421
|
-
systemTags.push("protected");
|
|
1422
|
-
}
|
|
1423
|
-
if (attrs.template) {
|
|
1424
|
-
systemTags.push("ApplyTemplate");
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
systemTags = this.normalizeSystemTags(systemTags);
|
|
1428
|
-
const contentTags = attrs.contentTags || attrs.tags || [];
|
|
1429
|
-
this.stm[key] = {
|
|
1430
|
-
value: entry.value,
|
|
1431
|
-
attributes: {
|
|
1432
|
-
...attrs,
|
|
1433
|
-
contentTags,
|
|
1434
|
-
systemTags,
|
|
1435
|
-
zIndex: attrs.zIndex ?? 0,
|
|
1436
|
-
// Sync legacy attributes FROM normalized systemTags
|
|
1437
|
-
tags: contentTags,
|
|
1438
|
-
readonly: systemTags.includes("readonly") || !systemTags.includes("LLMWrite"),
|
|
1439
|
-
visible: this.hasSystemPrompt(systemTags),
|
|
1440
|
-
hardcoded: systemTags.includes("protected"),
|
|
1441
|
-
template: systemTags.includes("ApplyTemplate") || systemTags.includes("template")
|
|
1442
|
-
}
|
|
1443
|
-
};
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
this.notifyGlobalListeners();
|
|
1447
|
-
this._isRemoteUpdate = false;
|
|
1448
1171
|
}
|
|
1172
|
+
return void 0;
|
|
1449
1173
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
sortedKeys.forEach((key) => {
|
|
1455
|
-
const entry = this.stm[key];
|
|
1456
|
-
if (this.hasLLMRead(entry.attributes.systemTags) || entry.attributes.visible) {
|
|
1457
|
-
if (entry.attributes.type === "image") {
|
|
1458
|
-
promptLines.push(`image ${key} available`);
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
if (entry.attributes.type === "file") {
|
|
1462
|
-
const canWrite2 = this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
|
|
1463
|
-
if (!canWrite2) {
|
|
1464
|
-
promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}`);
|
|
1465
|
-
} else {
|
|
1466
|
-
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1467
|
-
promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}. You can update this ${entry.attributes.type} using the write_${sanitizedKey} tool.`);
|
|
1468
|
-
}
|
|
1469
|
-
return;
|
|
1470
|
-
}
|
|
1471
|
-
const value = this.get_value(key);
|
|
1472
|
-
const formattedValue = typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
1473
|
-
const canWrite = this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
|
|
1474
|
-
if (!canWrite) {
|
|
1475
|
-
promptLines.push(`${key}: ${formattedValue}`);
|
|
1476
|
-
} else {
|
|
1477
|
-
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1478
|
-
const toolInstruction = `You can rewrite "${key}" by using the write_${sanitizedKey} tool. This tool DOES NOT append \u2014 start your response with the old value (${formattedValue})`;
|
|
1479
|
-
promptLines.push(`${key}: ${formattedValue}. ${toolInstruction}`);
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
});
|
|
1483
|
-
promptLines.push(`$date: ${now.toISOString().split("T")[0]}`);
|
|
1484
|
-
promptLines.push(`$time: ${now.toTimeString().split(" ")[0]}`);
|
|
1485
|
-
return promptLines.join("\n");
|
|
1486
|
-
}
|
|
1487
|
-
findKeyFromToolName(toolName) {
|
|
1488
|
-
if (!toolName.startsWith("write_")) {
|
|
1489
|
-
return void 0;
|
|
1490
|
-
}
|
|
1491
|
-
const sanitizedKey = toolName.replace("write_", "");
|
|
1492
|
-
const sortedKeys = this.getSortedKeys();
|
|
1493
|
-
return sortedKeys.find(
|
|
1494
|
-
(k) => k.replace(/[^a-zA-Z0-9_-]/g, "_") === sanitizedKey
|
|
1495
|
-
);
|
|
1496
|
-
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Generate Vercel AI SDK compatible tools for writable keys.
|
|
1176
|
+
* For document type keys, generates additional tools: append_, insert_, edit_
|
|
1177
|
+
*/
|
|
1497
1178
|
get_aisdk_tools() {
|
|
1498
1179
|
const tools = {};
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
return this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly");
|
|
1503
|
-
});
|
|
1504
|
-
writableKeys.forEach((key) => {
|
|
1505
|
-
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1506
|
-
const toolName = `write_${sanitizedKey}`;
|
|
1507
|
-
const entry = this.stm[key];
|
|
1508
|
-
const keyType = entry?.attributes.type || "text";
|
|
1509
|
-
let inputSchema;
|
|
1510
|
-
let description = `Write a value to the STM key: ${key}`;
|
|
1511
|
-
if (keyType === "image" || keyType === "file") {
|
|
1512
|
-
description += " (expects base64 encoded data)";
|
|
1513
|
-
inputSchema = zod.z.object({
|
|
1514
|
-
value: zod.z.string().describe(`Base64 encoded data for ${key}`),
|
|
1515
|
-
contentType: zod.z.string().optional().describe(`MIME type for the ${keyType}`)
|
|
1516
|
-
});
|
|
1517
|
-
} else if (keyType === "json") {
|
|
1518
|
-
description += " (expects JSON string)";
|
|
1519
|
-
inputSchema = zod.z.object({
|
|
1520
|
-
value: zod.z.string().describe(`JSON string value for ${key}`)
|
|
1521
|
-
});
|
|
1522
|
-
} else {
|
|
1523
|
-
inputSchema = zod.z.object({
|
|
1524
|
-
value: zod.z.string().describe(`The text value to write to ${key}`)
|
|
1525
|
-
});
|
|
1180
|
+
for (const [key, val] of this.rootMap) {
|
|
1181
|
+
if (key.startsWith("$")) {
|
|
1182
|
+
continue;
|
|
1526
1183
|
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
resultMessage = `Successfully saved image to ${key}`;
|
|
1548
|
-
} else if (keyType === "file") {
|
|
1549
|
-
resultMessage = `Successfully saved file to ${key}`;
|
|
1550
|
-
} else if (keyType === "json") {
|
|
1551
|
-
resultMessage = `Successfully saved JSON data to ${key}`;
|
|
1184
|
+
const entryMap = val;
|
|
1185
|
+
const attributes = entryMap.get("attributes");
|
|
1186
|
+
const isWritable = !attributes?.readonly && (attributes?.systemTags?.includes("LLMWrite") || !attributes?.systemTags);
|
|
1187
|
+
if (!isWritable) {
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
const sanitizedKey = this.sanitizeKeyForTool(key);
|
|
1191
|
+
const isDocument = attributes?.type === "document";
|
|
1192
|
+
tools[`write_${sanitizedKey}`] = {
|
|
1193
|
+
description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
|
|
1194
|
+
inputSchema: {
|
|
1195
|
+
type: "object",
|
|
1196
|
+
properties: {
|
|
1197
|
+
value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
|
|
1198
|
+
},
|
|
1199
|
+
required: ["value"]
|
|
1200
|
+
},
|
|
1201
|
+
execute: async ({ value }) => {
|
|
1202
|
+
if (isDocument) {
|
|
1203
|
+
this.replace_document_text(key, value);
|
|
1552
1204
|
} else {
|
|
1553
|
-
|
|
1205
|
+
this.set_value(key, value);
|
|
1554
1206
|
}
|
|
1555
1207
|
return {
|
|
1556
|
-
result:
|
|
1208
|
+
result: `Successfully wrote "${value}" to ${key}`,
|
|
1557
1209
|
key,
|
|
1558
|
-
value
|
|
1559
|
-
type: keyType,
|
|
1560
|
-
contentType: input.contentType,
|
|
1561
|
-
sanitizedKey
|
|
1210
|
+
value
|
|
1562
1211
|
};
|
|
1563
1212
|
}
|
|
1564
1213
|
};
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1214
|
+
if (isDocument) {
|
|
1215
|
+
tools[`append_${sanitizedKey}`] = {
|
|
1216
|
+
description: `Append text to the end of "${key}" document`,
|
|
1217
|
+
inputSchema: {
|
|
1218
|
+
type: "object",
|
|
1219
|
+
properties: {
|
|
1220
|
+
text: { type: "string", description: "Text to append" }
|
|
1221
|
+
},
|
|
1222
|
+
required: ["text"]
|
|
1223
|
+
},
|
|
1224
|
+
execute: async ({ text }) => {
|
|
1225
|
+
const yText = this.get_document(key);
|
|
1226
|
+
if (yText) {
|
|
1227
|
+
yText.insert(yText.length, text);
|
|
1228
|
+
return {
|
|
1229
|
+
result: `Successfully appended to ${key}`,
|
|
1230
|
+
key,
|
|
1231
|
+
appended: text
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
return { result: `Document ${key} not found`, key };
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
tools[`insert_${sanitizedKey}`] = {
|
|
1238
|
+
description: `Insert text at a position in "${key}" document`,
|
|
1239
|
+
inputSchema: {
|
|
1240
|
+
type: "object",
|
|
1241
|
+
properties: {
|
|
1242
|
+
index: { type: "number", description: "Position to insert at (0 = start)" },
|
|
1243
|
+
text: { type: "string", description: "Text to insert" }
|
|
1244
|
+
},
|
|
1245
|
+
required: ["index", "text"]
|
|
1246
|
+
},
|
|
1247
|
+
execute: async ({ index, text }) => {
|
|
1248
|
+
this.insert_text(key, index, text);
|
|
1249
|
+
return {
|
|
1250
|
+
result: `Successfully inserted text at position ${index} in ${key}`,
|
|
1251
|
+
key,
|
|
1252
|
+
index,
|
|
1253
|
+
inserted: text
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
tools[`edit_${sanitizedKey}`] = {
|
|
1258
|
+
description: `Find and replace text in "${key}" document`,
|
|
1259
|
+
inputSchema: {
|
|
1260
|
+
type: "object",
|
|
1261
|
+
properties: {
|
|
1262
|
+
find: { type: "string", description: "Text to find" },
|
|
1263
|
+
replace: { type: "string", description: "Replacement text" }
|
|
1264
|
+
},
|
|
1265
|
+
required: ["find", "replace"]
|
|
1266
|
+
},
|
|
1267
|
+
execute: async ({ find, replace }) => {
|
|
1268
|
+
const yText = this.get_document(key);
|
|
1269
|
+
if (yText) {
|
|
1270
|
+
const text = yText.toString();
|
|
1271
|
+
const idx = text.indexOf(find);
|
|
1272
|
+
if (idx !== -1) {
|
|
1273
|
+
yText.delete(idx, find.length);
|
|
1274
|
+
yText.insert(idx, replace);
|
|
1275
|
+
return {
|
|
1276
|
+
result: `Successfully replaced "${find}" with "${replace}" in ${key}`,
|
|
1277
|
+
key,
|
|
1278
|
+
find,
|
|
1279
|
+
replace,
|
|
1280
|
+
index: idx
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
return { result: `Text "${find}" not found in ${key}`, key };
|
|
1284
|
+
}
|
|
1285
|
+
return { result: `Document ${key} not found`, key };
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1568
1289
|
}
|
|
1569
1290
|
return tools;
|
|
1570
1291
|
}
|
|
1571
|
-
executeToolCall(toolName, value) {
|
|
1572
|
-
const originalKey = this.findKeyFromToolName(toolName);
|
|
1573
|
-
if (!originalKey) {
|
|
1574
|
-
return null;
|
|
1575
|
-
}
|
|
1576
|
-
const entry = this.stm[originalKey];
|
|
1577
|
-
const canWrite = entry && (this.hasLLMWrite(entry.attributes.systemTags) || !entry.attributes.readonly && !entry.attributes.systemTags.includes("readonly"));
|
|
1578
|
-
if (!canWrite) {
|
|
1579
|
-
return null;
|
|
1580
|
-
}
|
|
1581
|
-
this.set_value(originalKey, value);
|
|
1582
|
-
return {
|
|
1583
|
-
result: `Successfully wrote "${value}" to ${originalKey}`,
|
|
1584
|
-
key: originalKey,
|
|
1585
|
-
value
|
|
1586
|
-
};
|
|
1587
|
-
}
|
|
1588
|
-
// ============================================
|
|
1589
|
-
// Content Tag Methods (available to all access levels)
|
|
1590
|
-
// ============================================
|
|
1591
|
-
/**
|
|
1592
|
-
* Add a content tag to a key (user-level organization)
|
|
1593
|
-
*/
|
|
1594
|
-
addTag(key, tag) {
|
|
1595
|
-
if (key === "$date" || key === "$time") {
|
|
1596
|
-
return false;
|
|
1597
|
-
}
|
|
1598
|
-
const entry = this.stm[key];
|
|
1599
|
-
if (!entry) {
|
|
1600
|
-
return false;
|
|
1601
|
-
}
|
|
1602
|
-
if (!entry.attributes.contentTags) {
|
|
1603
|
-
entry.attributes.contentTags = [];
|
|
1604
|
-
}
|
|
1605
|
-
if (!entry.attributes.contentTags.includes(tag)) {
|
|
1606
|
-
entry.attributes.contentTags.push(tag);
|
|
1607
|
-
entry.attributes.tags = [...entry.attributes.contentTags];
|
|
1608
|
-
this.notifyGlobalListeners();
|
|
1609
|
-
return true;
|
|
1610
|
-
}
|
|
1611
|
-
return false;
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Remove a content tag from a key
|
|
1615
|
-
*/
|
|
1616
|
-
removeTag(key, tag) {
|
|
1617
|
-
if (key === "$date" || key === "$time") {
|
|
1618
|
-
return false;
|
|
1619
|
-
}
|
|
1620
|
-
const entry = this.stm[key];
|
|
1621
|
-
if (!entry || !entry.attributes.contentTags) {
|
|
1622
|
-
return false;
|
|
1623
|
-
}
|
|
1624
|
-
const tagIndex = entry.attributes.contentTags.indexOf(tag);
|
|
1625
|
-
if (tagIndex > -1) {
|
|
1626
|
-
entry.attributes.contentTags.splice(tagIndex, 1);
|
|
1627
|
-
entry.attributes.tags = [...entry.attributes.contentTags];
|
|
1628
|
-
this.notifyGlobalListeners();
|
|
1629
|
-
return true;
|
|
1630
|
-
}
|
|
1631
|
-
return false;
|
|
1632
|
-
}
|
|
1633
1292
|
/**
|
|
1634
|
-
*
|
|
1293
|
+
* Generate a system prompt containing all visible STM keys and their values.
|
|
1294
|
+
* Indicates which tools can be used to modify writable keys.
|
|
1635
1295
|
*/
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
return entry?.attributes.contentTags || [];
|
|
1642
|
-
}
|
|
1643
|
-
/**
|
|
1644
|
-
* Get all unique content tags across all keys
|
|
1645
|
-
*/
|
|
1646
|
-
getAllTags() {
|
|
1647
|
-
const allTags = /* @__PURE__ */ new Set();
|
|
1648
|
-
Object.values(this.stm).forEach((entry) => {
|
|
1649
|
-
if (entry.attributes.contentTags) {
|
|
1650
|
-
entry.attributes.contentTags.forEach((tag) => allTags.add(tag));
|
|
1296
|
+
get_system_prompt() {
|
|
1297
|
+
const lines = [];
|
|
1298
|
+
for (const [key, val] of this.rootMap) {
|
|
1299
|
+
if (key.startsWith("$")) {
|
|
1300
|
+
continue;
|
|
1651
1301
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
*/
|
|
1658
|
-
hasTag(key, tag) {
|
|
1659
|
-
if (key === "$date" || key === "$time") {
|
|
1660
|
-
return false;
|
|
1661
|
-
}
|
|
1662
|
-
const entry = this.stm[key];
|
|
1663
|
-
return entry?.attributes.contentTags?.includes(tag) || false;
|
|
1664
|
-
}
|
|
1665
|
-
/**
|
|
1666
|
-
* Get all keys with a specific content tag as formatted string
|
|
1667
|
-
*/
|
|
1668
|
-
getTagged(tag) {
|
|
1669
|
-
const entries = [];
|
|
1670
|
-
const sortedKeys = this.getSortedKeys();
|
|
1671
|
-
sortedKeys.forEach((key) => {
|
|
1672
|
-
const entry = this.stm[key];
|
|
1673
|
-
if (entry.attributes.contentTags?.includes(tag)) {
|
|
1674
|
-
entries.push([key, this.get_value(key)]);
|
|
1302
|
+
const entryMap = val;
|
|
1303
|
+
const attributes = entryMap.get("attributes");
|
|
1304
|
+
const isVisible = attributes?.visible !== false && (attributes?.systemTags?.includes("prompt") || attributes?.systemTags?.includes("SystemPrompt") || !attributes?.systemTags);
|
|
1305
|
+
if (!isVisible) {
|
|
1306
|
+
continue;
|
|
1675
1307
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
});
|
|
1688
|
-
}
|
|
1689
|
-
// ============================================
|
|
1690
|
-
// System Tag Methods (requires system access level)
|
|
1691
|
-
// ============================================
|
|
1692
|
-
/**
|
|
1693
|
-
* Add a system tag to a key (requires system access)
|
|
1694
|
-
* System tags: 'prompt', 'readonly', 'protected', 'template'
|
|
1695
|
-
*/
|
|
1696
|
-
systemAddTag(key, tag) {
|
|
1697
|
-
if (!this.hasSystemAccess) {
|
|
1698
|
-
console.warn("MindCache: systemAddTag requires system access level");
|
|
1699
|
-
return false;
|
|
1700
|
-
}
|
|
1701
|
-
if (key === "$date" || key === "$time") {
|
|
1702
|
-
return false;
|
|
1703
|
-
}
|
|
1704
|
-
const entry = this.stm[key];
|
|
1705
|
-
if (!entry) {
|
|
1706
|
-
return false;
|
|
1707
|
-
}
|
|
1708
|
-
if (!entry.attributes.systemTags) {
|
|
1709
|
-
entry.attributes.systemTags = [];
|
|
1710
|
-
}
|
|
1711
|
-
if (!entry.attributes.systemTags.includes(tag)) {
|
|
1712
|
-
entry.attributes.systemTags.push(tag);
|
|
1713
|
-
this.syncLegacyFromSystemTags(entry);
|
|
1714
|
-
this.notifyGlobalListeners();
|
|
1715
|
-
return true;
|
|
1716
|
-
}
|
|
1717
|
-
return false;
|
|
1718
|
-
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Remove a system tag from a key (requires system access)
|
|
1721
|
-
*/
|
|
1722
|
-
systemRemoveTag(key, tag) {
|
|
1723
|
-
if (!this.hasSystemAccess) {
|
|
1724
|
-
console.warn("MindCache: systemRemoveTag requires system access level");
|
|
1725
|
-
return false;
|
|
1726
|
-
}
|
|
1727
|
-
if (key === "$date" || key === "$time") {
|
|
1728
|
-
return false;
|
|
1729
|
-
}
|
|
1730
|
-
const entry = this.stm[key];
|
|
1731
|
-
if (!entry || !entry.attributes.systemTags) {
|
|
1732
|
-
return false;
|
|
1733
|
-
}
|
|
1734
|
-
const isHardcoded = entry.attributes.hardcoded || entry.attributes.systemTags.includes("protected");
|
|
1735
|
-
if (tag === "protected" && isHardcoded) {
|
|
1736
|
-
return false;
|
|
1737
|
-
}
|
|
1738
|
-
const tagIndex = entry.attributes.systemTags.indexOf(tag);
|
|
1739
|
-
if (tagIndex > -1) {
|
|
1740
|
-
entry.attributes.systemTags.splice(tagIndex, 1);
|
|
1741
|
-
this.syncLegacyFromSystemTags(entry);
|
|
1742
|
-
if (isHardcoded) {
|
|
1743
|
-
if (!entry.attributes.systemTags.includes("protected")) {
|
|
1744
|
-
entry.attributes.systemTags.push("protected");
|
|
1308
|
+
const value = this.get_value(key);
|
|
1309
|
+
const displayValue = typeof value === "object" ? JSON.stringify(value) : value;
|
|
1310
|
+
const isWritable = !attributes?.readonly && (attributes?.systemTags?.includes("LLMWrite") || !attributes?.systemTags);
|
|
1311
|
+
const isDocument = attributes?.type === "document";
|
|
1312
|
+
const sanitizedKey = this.sanitizeKeyForTool(key);
|
|
1313
|
+
if (isWritable) {
|
|
1314
|
+
if (isDocument) {
|
|
1315
|
+
lines.push(`${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`);
|
|
1316
|
+
} else {
|
|
1317
|
+
const oldValueHint = displayValue ? ` This tool DOES NOT append \u2014 start your response with the old value (${displayValue})` : "";
|
|
1318
|
+
lines.push(`${key}: ${displayValue}. You can rewrite "${key}" by using the write_${sanitizedKey} tool.${oldValueHint}`);
|
|
1745
1319
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
entry.attributes.template = false;
|
|
1320
|
+
} else {
|
|
1321
|
+
lines.push(`${key}: ${displayValue}`);
|
|
1749
1322
|
}
|
|
1750
|
-
this.notifyGlobalListeners();
|
|
1751
|
-
return true;
|
|
1752
|
-
}
|
|
1753
|
-
return false;
|
|
1754
|
-
}
|
|
1755
|
-
/**
|
|
1756
|
-
* Get all system tags for a key (requires system access)
|
|
1757
|
-
*/
|
|
1758
|
-
systemGetTags(key) {
|
|
1759
|
-
if (!this.hasSystemAccess) {
|
|
1760
|
-
console.warn("MindCache: systemGetTags requires system access level");
|
|
1761
|
-
return [];
|
|
1762
|
-
}
|
|
1763
|
-
if (key === "$date" || key === "$time") {
|
|
1764
|
-
return [];
|
|
1765
|
-
}
|
|
1766
|
-
const entry = this.stm[key];
|
|
1767
|
-
return entry?.attributes.systemTags || [];
|
|
1768
|
-
}
|
|
1769
|
-
/**
|
|
1770
|
-
* Check if a key has a specific system tag (requires system access)
|
|
1771
|
-
*/
|
|
1772
|
-
systemHasTag(key, tag) {
|
|
1773
|
-
if (!this.hasSystemAccess) {
|
|
1774
|
-
console.warn("MindCache: systemHasTag requires system access level");
|
|
1775
|
-
return false;
|
|
1776
|
-
}
|
|
1777
|
-
if (key === "$date" || key === "$time") {
|
|
1778
|
-
return false;
|
|
1779
1323
|
}
|
|
1780
|
-
|
|
1781
|
-
|
|
1324
|
+
lines.push(`$date: ${this.get_value("$date")}`);
|
|
1325
|
+
lines.push(`$time: ${this.get_value("$time")}`);
|
|
1326
|
+
return lines.join(", ");
|
|
1782
1327
|
}
|
|
1783
1328
|
/**
|
|
1784
|
-
*
|
|
1329
|
+
* Execute a tool call by name with the given value.
|
|
1330
|
+
* Returns the result or null if tool not found.
|
|
1785
1331
|
*/
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
return
|
|
1790
|
-
}
|
|
1791
|
-
if (key === "$date" || key === "$time") {
|
|
1792
|
-
return false;
|
|
1332
|
+
executeToolCall(toolName, value) {
|
|
1333
|
+
const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
|
|
1334
|
+
if (!match) {
|
|
1335
|
+
return null;
|
|
1793
1336
|
}
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1337
|
+
const [, action, sanitizedKey] = match;
|
|
1338
|
+
const key = this.findKeyFromSanitizedTool(sanitizedKey);
|
|
1339
|
+
if (!key) {
|
|
1340
|
+
return null;
|
|
1797
1341
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
return true;
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
* Get all keys with a specific system tag (requires system access)
|
|
1805
|
-
*/
|
|
1806
|
-
systemGetKeysByTag(tag) {
|
|
1807
|
-
if (!this.hasSystemAccess) {
|
|
1808
|
-
console.warn("MindCache: systemGetKeysByTag requires system access level");
|
|
1809
|
-
return [];
|
|
1342
|
+
const entryMap = this.rootMap.get(key);
|
|
1343
|
+
if (!entryMap) {
|
|
1344
|
+
return null;
|
|
1810
1345
|
}
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
return
|
|
1815
|
-
});
|
|
1816
|
-
}
|
|
1817
|
-
/**
|
|
1818
|
-
* Helper to sync legacy boolean attributes from system tags
|
|
1819
|
-
*/
|
|
1820
|
-
syncLegacyFromSystemTags(entry) {
|
|
1821
|
-
const tags = entry.attributes.systemTags || [];
|
|
1822
|
-
entry.attributes.readonly = tags.includes("readonly");
|
|
1823
|
-
entry.attributes.visible = tags.includes("prompt");
|
|
1824
|
-
entry.attributes.hardcoded = tags.includes("protected");
|
|
1825
|
-
entry.attributes.template = tags.includes("ApplyTemplate") || tags.includes("template");
|
|
1826
|
-
}
|
|
1827
|
-
toMarkdown() {
|
|
1828
|
-
const now = /* @__PURE__ */ new Date();
|
|
1829
|
-
const lines = [];
|
|
1830
|
-
const appendixEntries = [];
|
|
1831
|
-
let appendixCounter = 0;
|
|
1832
|
-
lines.push("# MindCache STM Export");
|
|
1833
|
-
lines.push("");
|
|
1834
|
-
lines.push(`Export Date: ${now.toISOString().split("T")[0]}`);
|
|
1835
|
-
lines.push("");
|
|
1836
|
-
lines.push("---");
|
|
1837
|
-
lines.push("");
|
|
1838
|
-
lines.push("## STM Entries");
|
|
1839
|
-
lines.push("");
|
|
1840
|
-
const sortedKeys = this.getSortedKeys();
|
|
1841
|
-
sortedKeys.forEach((key) => {
|
|
1842
|
-
const entry = this.stm[key];
|
|
1843
|
-
if (entry.attributes.hardcoded) {
|
|
1844
|
-
return;
|
|
1845
|
-
}
|
|
1846
|
-
lines.push(`### ${key}`);
|
|
1847
|
-
const entryType = entry.attributes.type && entry.attributes.type !== "undefined" ? entry.attributes.type : "text";
|
|
1848
|
-
lines.push(`- **Type**: \`${entryType}\``);
|
|
1849
|
-
lines.push(`- **Readonly**: \`${entry.attributes.readonly}\``);
|
|
1850
|
-
lines.push(`- **Visible**: \`${entry.attributes.visible}\``);
|
|
1851
|
-
lines.push(`- **Template**: \`${entry.attributes.template}\``);
|
|
1852
|
-
lines.push(`- **Z-Index**: \`${entry.attributes.zIndex ?? 0}\``);
|
|
1853
|
-
if (entry.attributes.tags && entry.attributes.tags.length > 0) {
|
|
1854
|
-
lines.push(`- **Tags**: \`${entry.attributes.tags.join("`, `")}\``);
|
|
1855
|
-
}
|
|
1856
|
-
if (entry.attributes.contentType) {
|
|
1857
|
-
lines.push(`- **Content Type**: \`${entry.attributes.contentType}\``);
|
|
1858
|
-
}
|
|
1859
|
-
if (entryType === "image" || entryType === "file") {
|
|
1860
|
-
const label = String.fromCharCode(65 + appendixCounter);
|
|
1861
|
-
appendixCounter++;
|
|
1862
|
-
lines.push(`- **Value**: [See Appendix ${label}]`);
|
|
1863
|
-
appendixEntries.push({
|
|
1864
|
-
key,
|
|
1865
|
-
type: entryType,
|
|
1866
|
-
contentType: entry.attributes.contentType || "application/octet-stream",
|
|
1867
|
-
base64: entry.value,
|
|
1868
|
-
label
|
|
1869
|
-
});
|
|
1870
|
-
} else if (entryType === "json") {
|
|
1871
|
-
lines.push("- **Value**:");
|
|
1872
|
-
lines.push("```json");
|
|
1873
|
-
try {
|
|
1874
|
-
const jsonValue = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value, null, 2);
|
|
1875
|
-
lines.push(jsonValue);
|
|
1876
|
-
} catch {
|
|
1877
|
-
lines.push(String(entry.value));
|
|
1878
|
-
}
|
|
1879
|
-
lines.push("```");
|
|
1880
|
-
} else {
|
|
1881
|
-
const valueStr = String(entry.value);
|
|
1882
|
-
lines.push("- **Value**:");
|
|
1883
|
-
lines.push("```");
|
|
1884
|
-
lines.push(valueStr);
|
|
1885
|
-
lines.push("```");
|
|
1886
|
-
}
|
|
1887
|
-
lines.push("");
|
|
1888
|
-
lines.push("---");
|
|
1889
|
-
lines.push("");
|
|
1890
|
-
});
|
|
1891
|
-
if (appendixEntries.length > 0) {
|
|
1892
|
-
lines.push("## Appendix: Binary Data");
|
|
1893
|
-
lines.push("");
|
|
1894
|
-
appendixEntries.forEach(({ key, contentType, base64, label }) => {
|
|
1895
|
-
lines.push(`### Appendix ${label}: ${key}`);
|
|
1896
|
-
lines.push(`**Type**: ${contentType}`);
|
|
1897
|
-
lines.push("");
|
|
1898
|
-
lines.push("```");
|
|
1899
|
-
lines.push(base64);
|
|
1900
|
-
lines.push("```");
|
|
1901
|
-
lines.push("");
|
|
1902
|
-
lines.push("---");
|
|
1903
|
-
lines.push("");
|
|
1904
|
-
});
|
|
1346
|
+
const attributes = entryMap.get("attributes");
|
|
1347
|
+
const isWritable = !attributes?.readonly && (attributes?.systemTags?.includes("LLMWrite") || !attributes?.systemTags);
|
|
1348
|
+
if (!isWritable) {
|
|
1349
|
+
return null;
|
|
1905
1350
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
let currentSection = "header";
|
|
1912
|
-
let currentKey = null;
|
|
1913
|
-
let currentEntry = null;
|
|
1914
|
-
let inCodeBlock = false;
|
|
1915
|
-
let codeBlockContent = [];
|
|
1916
|
-
let codeBlockType = null;
|
|
1917
|
-
const appendixData = {};
|
|
1918
|
-
let currentAppendixKey = null;
|
|
1919
|
-
const pendingEntries = {};
|
|
1920
|
-
this.clear();
|
|
1921
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1922
|
-
const line = lines[i];
|
|
1923
|
-
const trimmed = line.trim();
|
|
1924
|
-
if (trimmed === "## STM Entries") {
|
|
1925
|
-
currentSection = "entries";
|
|
1926
|
-
continue;
|
|
1927
|
-
}
|
|
1928
|
-
if (trimmed === "## Appendix: Binary Data") {
|
|
1929
|
-
currentSection = "appendix";
|
|
1930
|
-
continue;
|
|
1931
|
-
}
|
|
1932
|
-
if (trimmed === "```" || trimmed === "```json") {
|
|
1933
|
-
if (!inCodeBlock) {
|
|
1934
|
-
inCodeBlock = true;
|
|
1935
|
-
codeBlockContent = [];
|
|
1936
|
-
codeBlockType = currentSection === "appendix" ? "base64" : trimmed === "```json" ? "json" : "value";
|
|
1351
|
+
const isDocument = attributes?.type === "document";
|
|
1352
|
+
switch (action) {
|
|
1353
|
+
case "write":
|
|
1354
|
+
if (isDocument) {
|
|
1355
|
+
this.replace_document_text(key, value);
|
|
1937
1356
|
} else {
|
|
1938
|
-
|
|
1939
|
-
const content = codeBlockContent.join("\n");
|
|
1940
|
-
if (currentSection === "appendix" && currentAppendixKey) {
|
|
1941
|
-
appendixData[currentAppendixKey].base64 = content;
|
|
1942
|
-
} else if (currentEntry && codeBlockType === "json") {
|
|
1943
|
-
currentEntry.value = content;
|
|
1944
|
-
} else if (currentEntry && codeBlockType === "value") {
|
|
1945
|
-
currentEntry.value = content;
|
|
1946
|
-
}
|
|
1947
|
-
codeBlockContent = [];
|
|
1948
|
-
codeBlockType = null;
|
|
1357
|
+
this.set_value(key, value);
|
|
1949
1358
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
if (
|
|
1959
|
-
|
|
1359
|
+
return {
|
|
1360
|
+
result: `Successfully wrote "${value}" to ${key}`,
|
|
1361
|
+
key,
|
|
1362
|
+
value
|
|
1363
|
+
};
|
|
1364
|
+
case "append":
|
|
1365
|
+
if (isDocument) {
|
|
1366
|
+
const yText = this.get_document(key);
|
|
1367
|
+
if (yText) {
|
|
1368
|
+
yText.insert(yText.length, value);
|
|
1369
|
+
return {
|
|
1370
|
+
result: `Successfully appended to ${key}`,
|
|
1371
|
+
key,
|
|
1372
|
+
value
|
|
1373
|
+
};
|
|
1960
1374
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1375
|
+
}
|
|
1376
|
+
return null;
|
|
1377
|
+
case "insert":
|
|
1378
|
+
if (isDocument && typeof value === "object" && value.index !== void 0 && value.text) {
|
|
1379
|
+
this.insert_text(key, value.index, value.text);
|
|
1380
|
+
return {
|
|
1381
|
+
result: `Successfully inserted at position ${value.index} in ${key}`,
|
|
1382
|
+
key,
|
|
1383
|
+
value: value.text
|
|
1970
1384
|
};
|
|
1971
|
-
} else if (trimmed.startsWith("- **Type**: `")) {
|
|
1972
|
-
const type = trimmed.match(/`([^`]+)`/)?.[1];
|
|
1973
|
-
if (currentEntry && type && type !== "undefined") {
|
|
1974
|
-
currentEntry.attributes.type = type;
|
|
1975
|
-
}
|
|
1976
|
-
} else if (trimmed.startsWith("- **Readonly**: `")) {
|
|
1977
|
-
const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
|
|
1978
|
-
if (currentEntry) {
|
|
1979
|
-
currentEntry.attributes.readonly = value;
|
|
1980
|
-
}
|
|
1981
|
-
} else if (trimmed.startsWith("- **Visible**: `")) {
|
|
1982
|
-
const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
|
|
1983
|
-
if (currentEntry) {
|
|
1984
|
-
currentEntry.attributes.visible = value;
|
|
1985
|
-
}
|
|
1986
|
-
} else if (trimmed.startsWith("- **Template**: `")) {
|
|
1987
|
-
const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
|
|
1988
|
-
if (currentEntry) {
|
|
1989
|
-
currentEntry.attributes.template = value;
|
|
1990
|
-
}
|
|
1991
|
-
} else if (trimmed.startsWith("- **Z-Index**: `")) {
|
|
1992
|
-
const zIndexStr = trimmed.match(/`([^`]+)`/)?.[1];
|
|
1993
|
-
if (currentEntry && zIndexStr) {
|
|
1994
|
-
const zIndex = parseInt(zIndexStr, 10);
|
|
1995
|
-
if (!isNaN(zIndex)) {
|
|
1996
|
-
currentEntry.attributes.zIndex = zIndex;
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
} else if (trimmed.startsWith("- **Tags**: `")) {
|
|
2000
|
-
const tagsStr = trimmed.substring(13, trimmed.length - 1);
|
|
2001
|
-
if (currentEntry) {
|
|
2002
|
-
currentEntry.attributes.tags = tagsStr.split("`, `");
|
|
2003
|
-
}
|
|
2004
|
-
} else if (trimmed.startsWith("- **Content Type**: `")) {
|
|
2005
|
-
const contentType = trimmed.match(/`([^`]+)`/)?.[1];
|
|
2006
|
-
if (currentEntry && contentType) {
|
|
2007
|
-
currentEntry.attributes.contentType = contentType;
|
|
2008
|
-
}
|
|
2009
|
-
} else if (trimmed.startsWith("- **Value**: `")) {
|
|
2010
|
-
const value = trimmed.substring(14, trimmed.length - 1);
|
|
2011
|
-
if (currentEntry) {
|
|
2012
|
-
currentEntry.value = value;
|
|
2013
|
-
}
|
|
2014
|
-
} else if (trimmed.startsWith("- **Value**: [See Appendix ")) {
|
|
2015
|
-
const labelMatch = trimmed.match(/Appendix ([A-Z])\]/);
|
|
2016
|
-
if (currentEntry && labelMatch && currentKey) {
|
|
2017
|
-
currentEntry.appendixLabel = labelMatch[1];
|
|
2018
|
-
currentEntry.value = "";
|
|
2019
|
-
}
|
|
2020
1385
|
}
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
if (
|
|
2024
|
-
const
|
|
2025
|
-
if (
|
|
2026
|
-
const
|
|
2027
|
-
const
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
1386
|
+
return null;
|
|
1387
|
+
case "edit":
|
|
1388
|
+
if (isDocument && typeof value === "object" && value.find && value.replace !== void 0) {
|
|
1389
|
+
const yText = this.get_document(key);
|
|
1390
|
+
if (yText) {
|
|
1391
|
+
const text = yText.toString();
|
|
1392
|
+
const idx = text.indexOf(value.find);
|
|
1393
|
+
if (idx !== -1) {
|
|
1394
|
+
yText.delete(idx, value.find.length);
|
|
1395
|
+
yText.insert(idx, value.replace);
|
|
1396
|
+
return {
|
|
1397
|
+
result: `Successfully replaced "${value.find}" with "${value.replace}" in ${key}`,
|
|
1398
|
+
key,
|
|
1399
|
+
value: value.replace
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
2035
1402
|
}
|
|
2036
1403
|
}
|
|
2037
|
-
|
|
1404
|
+
return null;
|
|
1405
|
+
default:
|
|
1406
|
+
return null;
|
|
2038
1407
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
const appendixInfo = appendixData[appendixKey];
|
|
2047
|
-
if (appendixInfo && appendixInfo.base64) {
|
|
2048
|
-
entry.value = appendixInfo.base64;
|
|
2049
|
-
if (!entry.attributes.contentType && appendixInfo.contentType) {
|
|
2050
|
-
entry.attributes.contentType = appendixInfo.contentType;
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
if (entry.value !== void 0 && entry.attributes) {
|
|
2055
|
-
const attrs = entry.attributes;
|
|
2056
|
-
if (attrs.tags && attrs.tags.length > 0 && (!attrs.contentTags || attrs.contentTags.length === 0)) {
|
|
2057
|
-
attrs.contentTags = [...attrs.tags];
|
|
2058
|
-
}
|
|
2059
|
-
if (!attrs.systemTags || attrs.systemTags.length === 0) {
|
|
2060
|
-
const systemTags = [];
|
|
2061
|
-
if (attrs.visible !== false) {
|
|
2062
|
-
systemTags.push("prompt");
|
|
2063
|
-
}
|
|
2064
|
-
if (attrs.readonly) {
|
|
2065
|
-
systemTags.push("readonly");
|
|
2066
|
-
} else {
|
|
2067
|
-
systemTags.push("LLMWrite");
|
|
2068
|
-
}
|
|
2069
|
-
if (attrs.hardcoded) {
|
|
2070
|
-
systemTags.push("protected");
|
|
2071
|
-
}
|
|
2072
|
-
if (attrs.template) {
|
|
2073
|
-
systemTags.push("template");
|
|
2074
|
-
}
|
|
2075
|
-
attrs.systemTags = this.normalizeSystemTags(systemTags);
|
|
2076
|
-
} else {
|
|
2077
|
-
attrs.systemTags = this.normalizeSystemTags(attrs.systemTags);
|
|
2078
|
-
}
|
|
2079
|
-
if (!attrs.contentTags) {
|
|
2080
|
-
attrs.contentTags = [];
|
|
2081
|
-
}
|
|
2082
|
-
if (!attrs.tags) {
|
|
2083
|
-
attrs.tags = [...attrs.contentTags];
|
|
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");
|
|
2090
|
-
this.stm[key] = {
|
|
2091
|
-
value: entry.value,
|
|
2092
|
-
attributes: attrs
|
|
2093
|
-
};
|
|
2094
|
-
}
|
|
2095
|
-
});
|
|
2096
|
-
this.notifyGlobalListeners();
|
|
1408
|
+
}
|
|
1409
|
+
// Internal method stub for legacy compatibility
|
|
1410
|
+
_setFromRemote(_key, _value, _attributes) {
|
|
1411
|
+
}
|
|
1412
|
+
_deleteFromRemote(_key) {
|
|
1413
|
+
}
|
|
1414
|
+
_clearFromRemote() {
|
|
2097
1415
|
}
|
|
2098
1416
|
};
|
|
2099
|
-
var mindcache = new MindCache();
|
|
2100
1417
|
|
|
2101
1418
|
// src/cloud/index.ts
|
|
2102
1419
|
init_CloudAdapter();
|
|
@@ -2141,7 +1458,6 @@ function useMindCache(options) {
|
|
|
2141
1458
|
|
|
2142
1459
|
exports.DEFAULT_KEY_ATTRIBUTES = DEFAULT_KEY_ATTRIBUTES;
|
|
2143
1460
|
exports.MindCache = MindCache;
|
|
2144
|
-
exports.mindcache = mindcache;
|
|
2145
1461
|
exports.useMindCache = useMindCache;
|
|
2146
1462
|
//# sourceMappingURL=index.js.map
|
|
2147
1463
|
//# sourceMappingURL=index.js.map
|