mindcache 3.5.3 → 3.7.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.
@@ -6,6 +6,8 @@ var decoding = require('lib0/decoding');
6
6
  var Y = require('yjs');
7
7
  var yIndexeddb = require('y-indexeddb');
8
8
  var diff = require('fast-diff');
9
+ var ai = require('ai');
10
+ var zod = require('zod');
9
11
 
10
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
13
 
@@ -211,7 +213,6 @@ var init_CloudAdapter = __esm({
211
213
  if (!config.baseUrl) {
212
214
  throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
213
215
  }
214
- this.validateConfig(config);
215
216
  this.setupNetworkDetection();
216
217
  }
217
218
  ws = null;
@@ -227,43 +228,6 @@ var init_CloudAdapter = __esm({
227
228
  handleOnline = null;
228
229
  handleOffline = null;
229
230
  _synced = false;
230
- /**
231
- * Validate configuration and warn about common mistakes
232
- */
233
- validateConfig(config) {
234
- const baseUrl = config.baseUrl;
235
- if (!baseUrl) {
236
- return;
237
- }
238
- console.log("\u2601\uFE0F MindCache Cloud Config:", {
239
- baseUrl,
240
- instanceId: config.instanceId,
241
- hasTokenProvider: !!config.tokenProvider,
242
- hasApiKey: !!config.apiKey
243
- });
244
- try {
245
- const url = new URL(baseUrl);
246
- if (url.hostname === "mindcache.dev") {
247
- console.error(
248
- '\u26A0\uFE0F MindCache Cloud WARNING: baseUrl is set to "mindcache.dev" but the API is at "api.mindcache.dev".\n Current: ' + baseUrl + "\n Expected: https://api.mindcache.dev\n This will cause WebSocket connection failures (404 errors)."
249
- );
250
- }
251
- if (url.protocol === "ws:" || url.protocol === "wss:") {
252
- console.warn(
253
- "\u26A0\uFE0F MindCache Cloud: baseUrl uses WebSocket protocol (" + url.protocol + "). Consider using http:// or https:// - CloudAdapter will handle the WebSocket upgrade automatically."
254
- );
255
- }
256
- if (url.hostname === "localhost" && url.port !== "8787" && url.port !== "3000") {
257
- console.warn(
258
- "\u26A0\uFE0F MindCache Cloud: localhost URL detected with non-standard port " + url.port + ". Default MindCache dev server runs on port 8787."
259
- );
260
- }
261
- const wsUrl = baseUrl.replace("https://", "wss://").replace("http://", "ws://");
262
- console.log("\u2601\uFE0F WebSocket will connect to:", wsUrl + "/sync/" + config.instanceId);
263
- } catch (e) {
264
- console.error("\u26A0\uFE0F MindCache Cloud: Invalid baseUrl format:", baseUrl);
265
- }
266
- }
267
231
  /** Browser network status - instantly updated via navigator.onLine */
268
232
  get isOnline() {
269
233
  return this._isOnline;
@@ -506,6 +470,76 @@ var DEFAULT_KEY_ATTRIBUTES = {
506
470
  zIndex: 0
507
471
  };
508
472
 
473
+ // src/core/SchemaParser.ts
474
+ var SchemaParser = class {
475
+ /**
476
+ * Parse a markdown schema string into a CustomTypeDefinition
477
+ * @param schema - Markdown schema string
478
+ * @returns Parsed type definition
479
+ * @throws Error if schema format is invalid
480
+ */
481
+ static parse(schema) {
482
+ const lines = schema.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
483
+ if (lines.length === 0) {
484
+ throw new Error("Schema cannot be empty");
485
+ }
486
+ const typeNameMatch = lines[0].match(/^#\s*(\w+)$/);
487
+ if (!typeNameMatch) {
488
+ throw new Error(`Invalid schema: first line must be "#TypeName", got "${lines[0]}"`);
489
+ }
490
+ const typeName = typeNameMatch[1];
491
+ const fields = [];
492
+ for (let i = 1; i < lines.length; i++) {
493
+ const line = lines[i];
494
+ const fieldMatch = line.match(/^\*\s*([^:]+):\s*(.+)$/);
495
+ if (fieldMatch) {
496
+ fields.push({
497
+ name: fieldMatch[1].trim(),
498
+ description: fieldMatch[2].trim()
499
+ });
500
+ }
501
+ }
502
+ if (fields.length === 0) {
503
+ throw new Error(`Schema "${typeName}" must have at least one field`);
504
+ }
505
+ return {
506
+ name: typeName,
507
+ fields,
508
+ rawSchema: schema
509
+ };
510
+ }
511
+ /**
512
+ * Generate a markdown representation of a type definition
513
+ * Useful for including in LLM prompts
514
+ */
515
+ static toMarkdown(typeDef) {
516
+ const lines = [`#${typeDef.name}`];
517
+ for (const field of typeDef.fields) {
518
+ lines.push(`* ${field.name}: ${field.description}`);
519
+ }
520
+ return lines.join("\n");
521
+ }
522
+ /**
523
+ * Generate a prompt-friendly description of the type
524
+ * More verbose than toMarkdown, better for LLM guidance
525
+ */
526
+ static toPromptDescription(typeDef) {
527
+ const fieldDescs = typeDef.fields.map((f) => ` - ${f.name}: ${f.description}`).join("\n");
528
+ return `Type "${typeDef.name}" with fields:
529
+ ${fieldDescs}`;
530
+ }
531
+ /**
532
+ * Generate an example value structure based on the type definition
533
+ */
534
+ static generateExample(typeDef) {
535
+ const lines = [`#${typeDef.name.toLowerCase()}`];
536
+ for (const field of typeDef.fields) {
537
+ lines.push(`* ${field.name}: [${field.description}]`);
538
+ }
539
+ return lines.join("\n");
540
+ }
541
+ };
542
+
509
543
  // src/core/MarkdownSerializer.ts
510
544
  var MarkdownSerializer = class {
511
545
  /**
@@ -852,18 +886,16 @@ var MarkdownSerializer = class {
852
886
  return result;
853
887
  }
854
888
  };
855
-
856
- // src/core/AIToolBuilder.ts
857
889
  var AIToolBuilder = class _AIToolBuilder {
858
890
  /**
859
- * Sanitize key name for use in tool names
860
- */
891
+ * Sanitize key name for use in tool names
892
+ */
861
893
  static sanitizeKeyForTool(key) {
862
894
  return key.replace(/[^a-zA-Z0-9_-]/g, "_");
863
895
  }
864
896
  /**
865
- * Find original key from sanitized tool name
866
- */
897
+ * Find original key from sanitized tool name
898
+ */
867
899
  static findKeyFromSanitizedTool(mc, sanitizedKey) {
868
900
  for (const key of mc.keys()) {
869
901
  if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
@@ -873,15 +905,174 @@ var AIToolBuilder = class _AIToolBuilder {
873
905
  return void 0;
874
906
  }
875
907
  /**
876
- * Generate Vercel AI SDK compatible tools for writable keys.
877
- * For document type keys, generates additional tools: append_, insert_, edit_
878
- *
879
- * Security: All tools use llm_set_key internally which:
880
- * - Only modifies VALUES, never attributes/systemTags
881
- * - Prevents LLMs from escalating privileges
882
- */
908
+ * Generate framework-agnostic tools with raw JSON Schema.
909
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, etc.
910
+ *
911
+ * Tool format:
912
+ * {
913
+ * description: string,
914
+ * parameters: { type: 'object', properties: {...}, required: [...] },
915
+ * execute: async (args) => result
916
+ * }
917
+ */
918
+ static createTools(mc) {
919
+ return _AIToolBuilder._buildTools(mc);
920
+ }
921
+ /**
922
+ * Generate Vercel AI SDK v5 compatible tools using Zod schemas.
923
+ * Uses tool() helper with Zod for full AI SDK v5 compatibility.
924
+ *
925
+ * Use this with: generateText(), streamText() from 'ai' package
926
+ */
883
927
  static createVercelAITools(mc) {
884
928
  const tools = {};
929
+ const registeredTypes = mc.getRegisteredTypes();
930
+ const typeDesc = registeredTypes.length > 0 ? `Optional type: ${registeredTypes.join(" | ")}` : "No types registered";
931
+ tools["create_key"] = ai.tool({
932
+ description: `Create a new key in MindCache. ${registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : ""}`,
933
+ inputSchema: zod.z.object({
934
+ key: zod.z.string().describe('The key name (e.g., "contact_john_doe")'),
935
+ value: zod.z.string().describe("The value (JSON string for structured data)"),
936
+ type: zod.z.string().optional().describe(typeDesc)
937
+ }),
938
+ execute: async ({ key, value, type }) => {
939
+ if (mc.has(key)) {
940
+ return { result: `Key "${key}" exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)}`, error: true };
941
+ }
942
+ if (type && !mc.getTypeSchema(type)) {
943
+ return { result: `Type "${type}" not registered`, error: true };
944
+ }
945
+ mc.set_value(key, value, { systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"] });
946
+ if (type) {
947
+ mc.setType(key, type);
948
+ }
949
+ return { result: `Created "${key}"${type ? ` (${type})` : ""}`, key, value };
950
+ }
951
+ });
952
+ for (const key of mc.keys()) {
953
+ if (key.startsWith("$") || !mc.keyMatchesContext(key)) {
954
+ continue;
955
+ }
956
+ const attrs = mc.get_attributes(key);
957
+ if (!attrs?.systemTags?.includes("LLMWrite")) {
958
+ continue;
959
+ }
960
+ const sanitized = _AIToolBuilder.sanitizeKeyForTool(key);
961
+ const customTypeName = mc.getKeyType(key);
962
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
963
+ let desc = `Write to "${key}"`;
964
+ if (customType) {
965
+ desc = `Write to "${key}" (${customTypeName}). ${SchemaParser.toPromptDescription(customType)}`;
966
+ }
967
+ tools[`write_${sanitized}`] = ai.tool({
968
+ description: desc,
969
+ inputSchema: zod.z.object({
970
+ value: zod.z.string().describe(customType ? `JSON following ${customTypeName} schema` : "Value to write")
971
+ }),
972
+ execute: async ({ value }) => {
973
+ const success = mc.llm_set_key(key, value);
974
+ return success ? { result: `Wrote to ${key}`, key, value } : { result: `Failed to write to ${key}`, error: true };
975
+ }
976
+ });
977
+ if (attrs?.type === "document") {
978
+ tools[`append_${sanitized}`] = ai.tool({
979
+ description: `Append to "${key}" document`,
980
+ inputSchema: zod.z.object({ text: zod.z.string().describe("Text to append") }),
981
+ execute: async ({ text }) => {
982
+ const yText = mc.get_document(key);
983
+ if (yText) {
984
+ yText.insert(yText.length, text);
985
+ return { result: "Appended", key };
986
+ }
987
+ return { result: "Not found", error: true };
988
+ }
989
+ });
990
+ tools[`insert_${sanitized}`] = ai.tool({
991
+ description: `Insert text at position in "${key}" document`,
992
+ inputSchema: zod.z.object({
993
+ index: zod.z.number().describe("Position (0 = start)"),
994
+ text: zod.z.string().describe("Text to insert")
995
+ }),
996
+ execute: async ({ index, text }) => {
997
+ mc.insert_text(key, index, text);
998
+ return { result: `Inserted at ${index}`, key };
999
+ }
1000
+ });
1001
+ tools[`edit_${sanitized}`] = ai.tool({
1002
+ description: `Find and replace in "${key}" document`,
1003
+ inputSchema: zod.z.object({
1004
+ find: zod.z.string().describe("Text to find"),
1005
+ replace: zod.z.string().describe("Replacement")
1006
+ }),
1007
+ execute: async ({ find, replace }) => {
1008
+ const yText = mc.get_document(key);
1009
+ if (yText) {
1010
+ const text = yText.toString();
1011
+ const idx = text.indexOf(find);
1012
+ if (idx !== -1) {
1013
+ yText.delete(idx, find.length);
1014
+ yText.insert(idx, replace);
1015
+ return { result: `Replaced "${find}"`, key };
1016
+ }
1017
+ return { result: `"${find}" not found`, error: true };
1018
+ }
1019
+ return { result: "Document not found", error: true };
1020
+ }
1021
+ });
1022
+ }
1023
+ }
1024
+ return tools;
1025
+ }
1026
+ /**
1027
+ * Internal: Build tools with raw JSON Schema (framework-agnostic).
1028
+ */
1029
+ static _buildTools(mc) {
1030
+ const tools = {};
1031
+ const registeredTypes = mc.getRegisteredTypes();
1032
+ const typeInfo = registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : "No custom types registered";
1033
+ tools["create_key"] = {
1034
+ description: `Create a new key in MindCache. ${typeInfo}. The new key will be readable and writable by the LLM.`,
1035
+ parameters: {
1036
+ type: "object",
1037
+ properties: {
1038
+ key: { type: "string", description: 'The key name to create (e.g., "contact_john_doe")' },
1039
+ value: { type: "string", description: "The value to store (use JSON string for structured data)" },
1040
+ type: {
1041
+ type: "string",
1042
+ description: registeredTypes.length > 0 ? `Optional: custom type name (${registeredTypes.join(" | ")})` : "Optional: custom type name (none registered)"
1043
+ }
1044
+ },
1045
+ required: ["key", "value"]
1046
+ },
1047
+ execute: async ({ key, value, type }) => {
1048
+ if (mc.has(key)) {
1049
+ return {
1050
+ result: `Key "${key}" already exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)} to update it.`,
1051
+ key,
1052
+ error: true
1053
+ };
1054
+ }
1055
+ if (type && !mc.getTypeSchema(type)) {
1056
+ return {
1057
+ result: `Type "${type}" is not registered. Available types: ${registeredTypes.join(", ") || "none"}`,
1058
+ key,
1059
+ error: true
1060
+ };
1061
+ }
1062
+ mc.set_value(key, value, {
1063
+ systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"]
1064
+ });
1065
+ if (type) {
1066
+ mc.setType(key, type);
1067
+ }
1068
+ return {
1069
+ result: `Successfully created key "${key}"${type ? ` with type "${type}"` : ""}`,
1070
+ key,
1071
+ value,
1072
+ type
1073
+ };
1074
+ }
1075
+ };
885
1076
  for (const key of mc.keys()) {
886
1077
  if (key.startsWith("$")) {
887
1078
  continue;
@@ -896,12 +1087,28 @@ var AIToolBuilder = class _AIToolBuilder {
896
1087
  }
897
1088
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
898
1089
  const isDocument = attributes?.type === "document";
1090
+ const customTypeName = mc.getKeyType(key);
1091
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1092
+ let writeDescription;
1093
+ if (customType) {
1094
+ const schemaGuidance = SchemaParser.toPromptDescription(customType);
1095
+ const example = SchemaParser.generateExample(customType);
1096
+ writeDescription = `Write a value to "${key}" that must follow this schema:
1097
+ ${schemaGuidance}
1098
+
1099
+ Example format:
1100
+ ${example}`;
1101
+ } else if (isDocument) {
1102
+ writeDescription = `Rewrite the entire "${key}" document`;
1103
+ } else {
1104
+ writeDescription = `Write a value to the STM key: ${key}`;
1105
+ }
899
1106
  tools[`write_${sanitizedKey}`] = {
900
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
901
- inputSchema: {
1107
+ description: writeDescription,
1108
+ parameters: {
902
1109
  type: "object",
903
1110
  properties: {
904
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1111
+ value: { type: "string", description: customType ? `Value following ${customTypeName} schema` : isDocument ? "New document content" : "The value to write" }
905
1112
  },
906
1113
  required: ["value"]
907
1114
  },
@@ -924,7 +1131,7 @@ var AIToolBuilder = class _AIToolBuilder {
924
1131
  if (isDocument) {
925
1132
  tools[`append_${sanitizedKey}`] = {
926
1133
  description: `Append text to the end of "${key}" document`,
927
- inputSchema: {
1134
+ parameters: {
928
1135
  type: "object",
929
1136
  properties: {
930
1137
  text: { type: "string", description: "Text to append" }
@@ -949,7 +1156,7 @@ var AIToolBuilder = class _AIToolBuilder {
949
1156
  };
950
1157
  tools[`insert_${sanitizedKey}`] = {
951
1158
  description: `Insert text at a position in "${key}" document`,
952
- inputSchema: {
1159
+ parameters: {
953
1160
  type: "object",
954
1161
  properties: {
955
1162
  index: { type: "number", description: "Position to insert at (0 = start)" },
@@ -972,7 +1179,7 @@ var AIToolBuilder = class _AIToolBuilder {
972
1179
  };
973
1180
  tools[`edit_${sanitizedKey}`] = {
974
1181
  description: `Find and replace text in "${key}" document`,
975
- inputSchema: {
1182
+ parameters: {
976
1183
  type: "object",
977
1184
  properties: {
978
1185
  find: { type: "string", description: "Text to find" },
@@ -1009,11 +1216,16 @@ var AIToolBuilder = class _AIToolBuilder {
1009
1216
  return tools;
1010
1217
  }
1011
1218
  /**
1012
- * Generate a system prompt containing all visible STM keys and their values.
1013
- * Indicates which tools can be used to modify writable keys.
1014
- */
1219
+ * Generate a system prompt containing all visible STM keys and their values.
1220
+ * Indicates which tools can be used to modify writable keys.
1221
+ */
1015
1222
  static getSystemPrompt(mc) {
1016
1223
  const lines = [];
1224
+ const registeredTypes = mc.getRegisteredTypes();
1225
+ if (registeredTypes.length > 0) {
1226
+ lines.push(`[create_key tool available - registered types: ${registeredTypes.join(", ")}]`);
1227
+ lines.push("");
1228
+ }
1017
1229
  for (const key of mc.keys()) {
1018
1230
  if (key.startsWith("$")) {
1019
1231
  continue;
@@ -1031,8 +1243,18 @@ var AIToolBuilder = class _AIToolBuilder {
1031
1243
  const isWritable = attributes?.systemTags?.includes("LLMWrite");
1032
1244
  const isDocument = attributes?.type === "document";
1033
1245
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
1246
+ const customTypeName = mc.getKeyType(key);
1247
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1034
1248
  if (isWritable) {
1035
- if (isDocument) {
1249
+ if (customType) {
1250
+ const schemaInfo = SchemaParser.toMarkdown(customType);
1251
+ lines.push(
1252
+ `${key} (type: ${customTypeName}): ${displayValue}
1253
+ Schema:
1254
+ ${schemaInfo}
1255
+ Tool: write_${sanitizedKey}`
1256
+ );
1257
+ } else if (isDocument) {
1036
1258
  lines.push(
1037
1259
  `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
1038
1260
  );
@@ -1043,15 +1265,19 @@ var AIToolBuilder = class _AIToolBuilder {
1043
1265
  );
1044
1266
  }
1045
1267
  } else {
1046
- lines.push(`${key}: ${displayValue}`);
1268
+ if (customTypeName) {
1269
+ lines.push(`${key} (type: ${customTypeName}): ${displayValue}`);
1270
+ } else {
1271
+ lines.push(`${key}: ${displayValue}`);
1272
+ }
1047
1273
  }
1048
1274
  }
1049
1275
  return lines.join("\n");
1050
1276
  }
1051
1277
  /**
1052
- * Execute a tool call by name with the given value.
1053
- * Returns the result or null if tool not found.
1054
- */
1278
+ * Execute a tool call by name with the given value.
1279
+ * Returns the result or null if tool not found.
1280
+ */
1055
1281
  static executeToolCall(mc, toolName, value) {
1056
1282
  const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
1057
1283
  if (!match) {
@@ -1378,7 +1604,7 @@ var MindCache = class {
1378
1604
  listeners = {};
1379
1605
  globalListeners = [];
1380
1606
  // Metadata
1381
- version = "3.3.2";
1607
+ version = "3.6.0";
1382
1608
  // Internal flag to prevent sync loops when receiving remote updates
1383
1609
  // (Less critical with Yjs but kept for API compat)
1384
1610
  normalizeSystemTags(tags) {
@@ -1415,6 +1641,8 @@ var MindCache = class {
1415
1641
  _history = [];
1416
1642
  _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
1417
1643
  _historyEnabled = false;
1644
+ // Custom type registry
1645
+ _typeRegistry = /* @__PURE__ */ new Map();
1418
1646
  constructor(options) {
1419
1647
  this.doc = options?.doc || new Y__namespace.Doc();
1420
1648
  this.rootMap = this.doc.getMap("mindcache");
@@ -2371,6 +2599,71 @@ var MindCache = class {
2371
2599
  systemGetKeysByTag(tag) {
2372
2600
  return TagManager.systemGetKeysByTag(this, tag);
2373
2601
  }
2602
+ // ============================================
2603
+ // Custom Type Methods
2604
+ // ============================================
2605
+ /**
2606
+ * Register a custom type with a markdown schema definition.
2607
+ *
2608
+ * Schema format:
2609
+ * ```
2610
+ * #TypeName
2611
+ * * fieldName: description of the field
2612
+ * * anotherField: description
2613
+ * ```
2614
+ *
2615
+ * @param name - Type name (e.g., 'Contact')
2616
+ * @param schema - Markdown schema definition
2617
+ * @throws Error if schema format is invalid
2618
+ */
2619
+ registerType(name, schema) {
2620
+ const typeDef = SchemaParser.parse(schema);
2621
+ typeDef.name = name;
2622
+ this._typeRegistry.set(name, typeDef);
2623
+ }
2624
+ /**
2625
+ * Assign a custom type to a key.
2626
+ * The key must exist and the type must be registered.
2627
+ * Also sets the underlying type to 'json' since custom types are structured JSON data.
2628
+ *
2629
+ * @param key - Key to assign type to
2630
+ * @param typeName - Registered type name
2631
+ * @throws Error if key doesn't exist or type is not registered
2632
+ */
2633
+ setType(key, typeName) {
2634
+ if (!this.rootMap.has(key)) {
2635
+ throw new Error(`Key "${key}" does not exist`);
2636
+ }
2637
+ if (!this._typeRegistry.has(typeName)) {
2638
+ throw new Error(`Type "${typeName}" is not registered. Use registerType() first.`);
2639
+ }
2640
+ this.set_attributes(key, { type: "json", customType: typeName });
2641
+ }
2642
+ /**
2643
+ * Get a registered type schema definition.
2644
+ *
2645
+ * @param typeName - Type name to look up
2646
+ * @returns The type definition or undefined if not registered
2647
+ */
2648
+ getTypeSchema(typeName) {
2649
+ return this._typeRegistry.get(typeName);
2650
+ }
2651
+ /**
2652
+ * Get all registered type names.
2653
+ */
2654
+ getRegisteredTypes() {
2655
+ return Array.from(this._typeRegistry.keys());
2656
+ }
2657
+ /**
2658
+ * Get the custom type assigned to a key.
2659
+ *
2660
+ * @param key - Key to check
2661
+ * @returns Type name or undefined if no custom type assigned
2662
+ */
2663
+ getKeyType(key) {
2664
+ const attrs = this.get_attributes(key);
2665
+ return attrs?.customType;
2666
+ }
2374
2667
  /**
2375
2668
  * Helper to get sorted keys (by zIndex).
2376
2669
  * Respects context filtering when set.
@@ -2632,9 +2925,28 @@ var MindCache = class {
2632
2925
  findKeyFromSanitizedTool(sanitizedKey) {
2633
2926
  return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
2634
2927
  }
2928
+ /**
2929
+ * Generate framework-agnostic tools with raw JSON Schema.
2930
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, and other frameworks.
2931
+ *
2932
+ * Tool format:
2933
+ * {
2934
+ * description: string,
2935
+ * parameters: { type: 'object', properties: {...}, required: [...] },
2936
+ * execute: async (args) => result
2937
+ * }
2938
+ *
2939
+ * Security: All tools use llm_set_key internally which:
2940
+ * - Only modifies VALUES, never attributes/systemTags
2941
+ * - Prevents LLMs from escalating privileges
2942
+ */
2943
+ create_tools() {
2944
+ return AIToolBuilder.createTools(this);
2945
+ }
2635
2946
  /**
2636
2947
  * Generate Vercel AI SDK compatible tools for writable keys.
2637
- * For document type keys, generates additional tools: append_, insert_, edit_
2948
+ * Wraps parameters with jsonSchema() for AI SDK v5 compatibility.
2949
+ * Use this with: generateText(), streamText() from 'ai' package.
2638
2950
  *
2639
2951
  * Security: All tools use llm_set_key internally which:
2640
2952
  * - Only modifies VALUES, never attributes/systemTags