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.
package/dist/server.mjs CHANGED
@@ -4,6 +4,8 @@ import * as decoding from 'lib0/decoding';
4
4
  import * as Y from 'yjs';
5
5
  import { IndexeddbPersistence } from 'y-indexeddb';
6
6
  import diff from 'fast-diff';
7
+ import { tool } from 'ai';
8
+ import { z } from 'zod';
7
9
 
8
10
  var __defProp = Object.defineProperty;
9
11
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -183,7 +185,6 @@ var init_CloudAdapter = __esm({
183
185
  if (!config.baseUrl) {
184
186
  throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
185
187
  }
186
- this.validateConfig(config);
187
188
  this.setupNetworkDetection();
188
189
  }
189
190
  ws = null;
@@ -199,43 +200,6 @@ var init_CloudAdapter = __esm({
199
200
  handleOnline = null;
200
201
  handleOffline = null;
201
202
  _synced = false;
202
- /**
203
- * Validate configuration and warn about common mistakes
204
- */
205
- validateConfig(config) {
206
- const baseUrl = config.baseUrl;
207
- if (!baseUrl) {
208
- return;
209
- }
210
- console.log("\u2601\uFE0F MindCache Cloud Config:", {
211
- baseUrl,
212
- instanceId: config.instanceId,
213
- hasTokenProvider: !!config.tokenProvider,
214
- hasApiKey: !!config.apiKey
215
- });
216
- try {
217
- const url = new URL(baseUrl);
218
- if (url.hostname === "mindcache.dev") {
219
- console.error(
220
- '\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)."
221
- );
222
- }
223
- if (url.protocol === "ws:" || url.protocol === "wss:") {
224
- console.warn(
225
- "\u26A0\uFE0F MindCache Cloud: baseUrl uses WebSocket protocol (" + url.protocol + "). Consider using http:// or https:// - CloudAdapter will handle the WebSocket upgrade automatically."
226
- );
227
- }
228
- if (url.hostname === "localhost" && url.port !== "8787" && url.port !== "3000") {
229
- console.warn(
230
- "\u26A0\uFE0F MindCache Cloud: localhost URL detected with non-standard port " + url.port + ". Default MindCache dev server runs on port 8787."
231
- );
232
- }
233
- const wsUrl = baseUrl.replace("https://", "wss://").replace("http://", "ws://");
234
- console.log("\u2601\uFE0F WebSocket will connect to:", wsUrl + "/sync/" + config.instanceId);
235
- } catch (e) {
236
- console.error("\u26A0\uFE0F MindCache Cloud: Invalid baseUrl format:", baseUrl);
237
- }
238
- }
239
203
  /** Browser network status - instantly updated via navigator.onLine */
240
204
  get isOnline() {
241
205
  return this._isOnline;
@@ -488,6 +452,76 @@ var SystemTagHelpers = {
488
452
  hasTemplateInjection: (attrs) => attrs.systemTags.includes("ApplyTemplate")
489
453
  };
490
454
 
455
+ // src/core/SchemaParser.ts
456
+ var SchemaParser = class {
457
+ /**
458
+ * Parse a markdown schema string into a CustomTypeDefinition
459
+ * @param schema - Markdown schema string
460
+ * @returns Parsed type definition
461
+ * @throws Error if schema format is invalid
462
+ */
463
+ static parse(schema) {
464
+ const lines = schema.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
465
+ if (lines.length === 0) {
466
+ throw new Error("Schema cannot be empty");
467
+ }
468
+ const typeNameMatch = lines[0].match(/^#\s*(\w+)$/);
469
+ if (!typeNameMatch) {
470
+ throw new Error(`Invalid schema: first line must be "#TypeName", got "${lines[0]}"`);
471
+ }
472
+ const typeName = typeNameMatch[1];
473
+ const fields = [];
474
+ for (let i = 1; i < lines.length; i++) {
475
+ const line = lines[i];
476
+ const fieldMatch = line.match(/^\*\s*([^:]+):\s*(.+)$/);
477
+ if (fieldMatch) {
478
+ fields.push({
479
+ name: fieldMatch[1].trim(),
480
+ description: fieldMatch[2].trim()
481
+ });
482
+ }
483
+ }
484
+ if (fields.length === 0) {
485
+ throw new Error(`Schema "${typeName}" must have at least one field`);
486
+ }
487
+ return {
488
+ name: typeName,
489
+ fields,
490
+ rawSchema: schema
491
+ };
492
+ }
493
+ /**
494
+ * Generate a markdown representation of a type definition
495
+ * Useful for including in LLM prompts
496
+ */
497
+ static toMarkdown(typeDef) {
498
+ const lines = [`#${typeDef.name}`];
499
+ for (const field of typeDef.fields) {
500
+ lines.push(`* ${field.name}: ${field.description}`);
501
+ }
502
+ return lines.join("\n");
503
+ }
504
+ /**
505
+ * Generate a prompt-friendly description of the type
506
+ * More verbose than toMarkdown, better for LLM guidance
507
+ */
508
+ static toPromptDescription(typeDef) {
509
+ const fieldDescs = typeDef.fields.map((f) => ` - ${f.name}: ${f.description}`).join("\n");
510
+ return `Type "${typeDef.name}" with fields:
511
+ ${fieldDescs}`;
512
+ }
513
+ /**
514
+ * Generate an example value structure based on the type definition
515
+ */
516
+ static generateExample(typeDef) {
517
+ const lines = [`#${typeDef.name.toLowerCase()}`];
518
+ for (const field of typeDef.fields) {
519
+ lines.push(`* ${field.name}: [${field.description}]`);
520
+ }
521
+ return lines.join("\n");
522
+ }
523
+ };
524
+
491
525
  // src/core/MarkdownSerializer.ts
492
526
  var MarkdownSerializer = class {
493
527
  /**
@@ -834,18 +868,16 @@ var MarkdownSerializer = class {
834
868
  return result;
835
869
  }
836
870
  };
837
-
838
- // src/core/AIToolBuilder.ts
839
871
  var AIToolBuilder = class _AIToolBuilder {
840
872
  /**
841
- * Sanitize key name for use in tool names
842
- */
873
+ * Sanitize key name for use in tool names
874
+ */
843
875
  static sanitizeKeyForTool(key) {
844
876
  return key.replace(/[^a-zA-Z0-9_-]/g, "_");
845
877
  }
846
878
  /**
847
- * Find original key from sanitized tool name
848
- */
879
+ * Find original key from sanitized tool name
880
+ */
849
881
  static findKeyFromSanitizedTool(mc, sanitizedKey) {
850
882
  for (const key of mc.keys()) {
851
883
  if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
@@ -855,15 +887,174 @@ var AIToolBuilder = class _AIToolBuilder {
855
887
  return void 0;
856
888
  }
857
889
  /**
858
- * Generate Vercel AI SDK compatible tools for writable keys.
859
- * For document type keys, generates additional tools: append_, insert_, edit_
860
- *
861
- * Security: All tools use llm_set_key internally which:
862
- * - Only modifies VALUES, never attributes/systemTags
863
- * - Prevents LLMs from escalating privileges
864
- */
890
+ * Generate framework-agnostic tools with raw JSON Schema.
891
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, etc.
892
+ *
893
+ * Tool format:
894
+ * {
895
+ * description: string,
896
+ * parameters: { type: 'object', properties: {...}, required: [...] },
897
+ * execute: async (args) => result
898
+ * }
899
+ */
900
+ static createTools(mc) {
901
+ return _AIToolBuilder._buildTools(mc);
902
+ }
903
+ /**
904
+ * Generate Vercel AI SDK v5 compatible tools using Zod schemas.
905
+ * Uses tool() helper with Zod for full AI SDK v5 compatibility.
906
+ *
907
+ * Use this with: generateText(), streamText() from 'ai' package
908
+ */
865
909
  static createVercelAITools(mc) {
866
910
  const tools = {};
911
+ const registeredTypes = mc.getRegisteredTypes();
912
+ const typeDesc = registeredTypes.length > 0 ? `Optional type: ${registeredTypes.join(" | ")}` : "No types registered";
913
+ tools["create_key"] = tool({
914
+ description: `Create a new key in MindCache. ${registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : ""}`,
915
+ inputSchema: z.object({
916
+ key: z.string().describe('The key name (e.g., "contact_john_doe")'),
917
+ value: z.string().describe("The value (JSON string for structured data)"),
918
+ type: z.string().optional().describe(typeDesc)
919
+ }),
920
+ execute: async ({ key, value, type }) => {
921
+ if (mc.has(key)) {
922
+ return { result: `Key "${key}" exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)}`, error: true };
923
+ }
924
+ if (type && !mc.getTypeSchema(type)) {
925
+ return { result: `Type "${type}" not registered`, error: true };
926
+ }
927
+ mc.set_value(key, value, { systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"] });
928
+ if (type) {
929
+ mc.setType(key, type);
930
+ }
931
+ return { result: `Created "${key}"${type ? ` (${type})` : ""}`, key, value };
932
+ }
933
+ });
934
+ for (const key of mc.keys()) {
935
+ if (key.startsWith("$") || !mc.keyMatchesContext(key)) {
936
+ continue;
937
+ }
938
+ const attrs = mc.get_attributes(key);
939
+ if (!attrs?.systemTags?.includes("LLMWrite")) {
940
+ continue;
941
+ }
942
+ const sanitized = _AIToolBuilder.sanitizeKeyForTool(key);
943
+ const customTypeName = mc.getKeyType(key);
944
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
945
+ let desc = `Write to "${key}"`;
946
+ if (customType) {
947
+ desc = `Write to "${key}" (${customTypeName}). ${SchemaParser.toPromptDescription(customType)}`;
948
+ }
949
+ tools[`write_${sanitized}`] = tool({
950
+ description: desc,
951
+ inputSchema: z.object({
952
+ value: z.string().describe(customType ? `JSON following ${customTypeName} schema` : "Value to write")
953
+ }),
954
+ execute: async ({ value }) => {
955
+ const success = mc.llm_set_key(key, value);
956
+ return success ? { result: `Wrote to ${key}`, key, value } : { result: `Failed to write to ${key}`, error: true };
957
+ }
958
+ });
959
+ if (attrs?.type === "document") {
960
+ tools[`append_${sanitized}`] = tool({
961
+ description: `Append to "${key}" document`,
962
+ inputSchema: z.object({ text: z.string().describe("Text to append") }),
963
+ execute: async ({ text }) => {
964
+ const yText = mc.get_document(key);
965
+ if (yText) {
966
+ yText.insert(yText.length, text);
967
+ return { result: "Appended", key };
968
+ }
969
+ return { result: "Not found", error: true };
970
+ }
971
+ });
972
+ tools[`insert_${sanitized}`] = tool({
973
+ description: `Insert text at position in "${key}" document`,
974
+ inputSchema: z.object({
975
+ index: z.number().describe("Position (0 = start)"),
976
+ text: z.string().describe("Text to insert")
977
+ }),
978
+ execute: async ({ index, text }) => {
979
+ mc.insert_text(key, index, text);
980
+ return { result: `Inserted at ${index}`, key };
981
+ }
982
+ });
983
+ tools[`edit_${sanitized}`] = tool({
984
+ description: `Find and replace in "${key}" document`,
985
+ inputSchema: z.object({
986
+ find: z.string().describe("Text to find"),
987
+ replace: z.string().describe("Replacement")
988
+ }),
989
+ execute: async ({ find, replace }) => {
990
+ const yText = mc.get_document(key);
991
+ if (yText) {
992
+ const text = yText.toString();
993
+ const idx = text.indexOf(find);
994
+ if (idx !== -1) {
995
+ yText.delete(idx, find.length);
996
+ yText.insert(idx, replace);
997
+ return { result: `Replaced "${find}"`, key };
998
+ }
999
+ return { result: `"${find}" not found`, error: true };
1000
+ }
1001
+ return { result: "Document not found", error: true };
1002
+ }
1003
+ });
1004
+ }
1005
+ }
1006
+ return tools;
1007
+ }
1008
+ /**
1009
+ * Internal: Build tools with raw JSON Schema (framework-agnostic).
1010
+ */
1011
+ static _buildTools(mc) {
1012
+ const tools = {};
1013
+ const registeredTypes = mc.getRegisteredTypes();
1014
+ const typeInfo = registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : "No custom types registered";
1015
+ tools["create_key"] = {
1016
+ description: `Create a new key in MindCache. ${typeInfo}. The new key will be readable and writable by the LLM.`,
1017
+ parameters: {
1018
+ type: "object",
1019
+ properties: {
1020
+ key: { type: "string", description: 'The key name to create (e.g., "contact_john_doe")' },
1021
+ value: { type: "string", description: "The value to store (use JSON string for structured data)" },
1022
+ type: {
1023
+ type: "string",
1024
+ description: registeredTypes.length > 0 ? `Optional: custom type name (${registeredTypes.join(" | ")})` : "Optional: custom type name (none registered)"
1025
+ }
1026
+ },
1027
+ required: ["key", "value"]
1028
+ },
1029
+ execute: async ({ key, value, type }) => {
1030
+ if (mc.has(key)) {
1031
+ return {
1032
+ result: `Key "${key}" already exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)} to update it.`,
1033
+ key,
1034
+ error: true
1035
+ };
1036
+ }
1037
+ if (type && !mc.getTypeSchema(type)) {
1038
+ return {
1039
+ result: `Type "${type}" is not registered. Available types: ${registeredTypes.join(", ") || "none"}`,
1040
+ key,
1041
+ error: true
1042
+ };
1043
+ }
1044
+ mc.set_value(key, value, {
1045
+ systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"]
1046
+ });
1047
+ if (type) {
1048
+ mc.setType(key, type);
1049
+ }
1050
+ return {
1051
+ result: `Successfully created key "${key}"${type ? ` with type "${type}"` : ""}`,
1052
+ key,
1053
+ value,
1054
+ type
1055
+ };
1056
+ }
1057
+ };
867
1058
  for (const key of mc.keys()) {
868
1059
  if (key.startsWith("$")) {
869
1060
  continue;
@@ -878,12 +1069,28 @@ var AIToolBuilder = class _AIToolBuilder {
878
1069
  }
879
1070
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
880
1071
  const isDocument = attributes?.type === "document";
1072
+ const customTypeName = mc.getKeyType(key);
1073
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1074
+ let writeDescription;
1075
+ if (customType) {
1076
+ const schemaGuidance = SchemaParser.toPromptDescription(customType);
1077
+ const example = SchemaParser.generateExample(customType);
1078
+ writeDescription = `Write a value to "${key}" that must follow this schema:
1079
+ ${schemaGuidance}
1080
+
1081
+ Example format:
1082
+ ${example}`;
1083
+ } else if (isDocument) {
1084
+ writeDescription = `Rewrite the entire "${key}" document`;
1085
+ } else {
1086
+ writeDescription = `Write a value to the STM key: ${key}`;
1087
+ }
881
1088
  tools[`write_${sanitizedKey}`] = {
882
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
883
- inputSchema: {
1089
+ description: writeDescription,
1090
+ parameters: {
884
1091
  type: "object",
885
1092
  properties: {
886
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1093
+ value: { type: "string", description: customType ? `Value following ${customTypeName} schema` : isDocument ? "New document content" : "The value to write" }
887
1094
  },
888
1095
  required: ["value"]
889
1096
  },
@@ -906,7 +1113,7 @@ var AIToolBuilder = class _AIToolBuilder {
906
1113
  if (isDocument) {
907
1114
  tools[`append_${sanitizedKey}`] = {
908
1115
  description: `Append text to the end of "${key}" document`,
909
- inputSchema: {
1116
+ parameters: {
910
1117
  type: "object",
911
1118
  properties: {
912
1119
  text: { type: "string", description: "Text to append" }
@@ -931,7 +1138,7 @@ var AIToolBuilder = class _AIToolBuilder {
931
1138
  };
932
1139
  tools[`insert_${sanitizedKey}`] = {
933
1140
  description: `Insert text at a position in "${key}" document`,
934
- inputSchema: {
1141
+ parameters: {
935
1142
  type: "object",
936
1143
  properties: {
937
1144
  index: { type: "number", description: "Position to insert at (0 = start)" },
@@ -954,7 +1161,7 @@ var AIToolBuilder = class _AIToolBuilder {
954
1161
  };
955
1162
  tools[`edit_${sanitizedKey}`] = {
956
1163
  description: `Find and replace text in "${key}" document`,
957
- inputSchema: {
1164
+ parameters: {
958
1165
  type: "object",
959
1166
  properties: {
960
1167
  find: { type: "string", description: "Text to find" },
@@ -991,11 +1198,16 @@ var AIToolBuilder = class _AIToolBuilder {
991
1198
  return tools;
992
1199
  }
993
1200
  /**
994
- * Generate a system prompt containing all visible STM keys and their values.
995
- * Indicates which tools can be used to modify writable keys.
996
- */
1201
+ * Generate a system prompt containing all visible STM keys and their values.
1202
+ * Indicates which tools can be used to modify writable keys.
1203
+ */
997
1204
  static getSystemPrompt(mc) {
998
1205
  const lines = [];
1206
+ const registeredTypes = mc.getRegisteredTypes();
1207
+ if (registeredTypes.length > 0) {
1208
+ lines.push(`[create_key tool available - registered types: ${registeredTypes.join(", ")}]`);
1209
+ lines.push("");
1210
+ }
999
1211
  for (const key of mc.keys()) {
1000
1212
  if (key.startsWith("$")) {
1001
1213
  continue;
@@ -1013,8 +1225,18 @@ var AIToolBuilder = class _AIToolBuilder {
1013
1225
  const isWritable = attributes?.systemTags?.includes("LLMWrite");
1014
1226
  const isDocument = attributes?.type === "document";
1015
1227
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
1228
+ const customTypeName = mc.getKeyType(key);
1229
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1016
1230
  if (isWritable) {
1017
- if (isDocument) {
1231
+ if (customType) {
1232
+ const schemaInfo = SchemaParser.toMarkdown(customType);
1233
+ lines.push(
1234
+ `${key} (type: ${customTypeName}): ${displayValue}
1235
+ Schema:
1236
+ ${schemaInfo}
1237
+ Tool: write_${sanitizedKey}`
1238
+ );
1239
+ } else if (isDocument) {
1018
1240
  lines.push(
1019
1241
  `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
1020
1242
  );
@@ -1025,15 +1247,19 @@ var AIToolBuilder = class _AIToolBuilder {
1025
1247
  );
1026
1248
  }
1027
1249
  } else {
1028
- lines.push(`${key}: ${displayValue}`);
1250
+ if (customTypeName) {
1251
+ lines.push(`${key} (type: ${customTypeName}): ${displayValue}`);
1252
+ } else {
1253
+ lines.push(`${key}: ${displayValue}`);
1254
+ }
1029
1255
  }
1030
1256
  }
1031
1257
  return lines.join("\n");
1032
1258
  }
1033
1259
  /**
1034
- * Execute a tool call by name with the given value.
1035
- * Returns the result or null if tool not found.
1036
- */
1260
+ * Execute a tool call by name with the given value.
1261
+ * Returns the result or null if tool not found.
1262
+ */
1037
1263
  static executeToolCall(mc, toolName, value) {
1038
1264
  const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
1039
1265
  if (!match) {
@@ -1360,7 +1586,7 @@ var MindCache = class {
1360
1586
  listeners = {};
1361
1587
  globalListeners = [];
1362
1588
  // Metadata
1363
- version = "3.3.2";
1589
+ version = "3.6.0";
1364
1590
  // Internal flag to prevent sync loops when receiving remote updates
1365
1591
  // (Less critical with Yjs but kept for API compat)
1366
1592
  normalizeSystemTags(tags) {
@@ -1397,6 +1623,8 @@ var MindCache = class {
1397
1623
  _history = [];
1398
1624
  _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
1399
1625
  _historyEnabled = false;
1626
+ // Custom type registry
1627
+ _typeRegistry = /* @__PURE__ */ new Map();
1400
1628
  constructor(options) {
1401
1629
  this.doc = options?.doc || new Y.Doc();
1402
1630
  this.rootMap = this.doc.getMap("mindcache");
@@ -2353,6 +2581,71 @@ var MindCache = class {
2353
2581
  systemGetKeysByTag(tag) {
2354
2582
  return TagManager.systemGetKeysByTag(this, tag);
2355
2583
  }
2584
+ // ============================================
2585
+ // Custom Type Methods
2586
+ // ============================================
2587
+ /**
2588
+ * Register a custom type with a markdown schema definition.
2589
+ *
2590
+ * Schema format:
2591
+ * ```
2592
+ * #TypeName
2593
+ * * fieldName: description of the field
2594
+ * * anotherField: description
2595
+ * ```
2596
+ *
2597
+ * @param name - Type name (e.g., 'Contact')
2598
+ * @param schema - Markdown schema definition
2599
+ * @throws Error if schema format is invalid
2600
+ */
2601
+ registerType(name, schema) {
2602
+ const typeDef = SchemaParser.parse(schema);
2603
+ typeDef.name = name;
2604
+ this._typeRegistry.set(name, typeDef);
2605
+ }
2606
+ /**
2607
+ * Assign a custom type to a key.
2608
+ * The key must exist and the type must be registered.
2609
+ * Also sets the underlying type to 'json' since custom types are structured JSON data.
2610
+ *
2611
+ * @param key - Key to assign type to
2612
+ * @param typeName - Registered type name
2613
+ * @throws Error if key doesn't exist or type is not registered
2614
+ */
2615
+ setType(key, typeName) {
2616
+ if (!this.rootMap.has(key)) {
2617
+ throw new Error(`Key "${key}" does not exist`);
2618
+ }
2619
+ if (!this._typeRegistry.has(typeName)) {
2620
+ throw new Error(`Type "${typeName}" is not registered. Use registerType() first.`);
2621
+ }
2622
+ this.set_attributes(key, { type: "json", customType: typeName });
2623
+ }
2624
+ /**
2625
+ * Get a registered type schema definition.
2626
+ *
2627
+ * @param typeName - Type name to look up
2628
+ * @returns The type definition or undefined if not registered
2629
+ */
2630
+ getTypeSchema(typeName) {
2631
+ return this._typeRegistry.get(typeName);
2632
+ }
2633
+ /**
2634
+ * Get all registered type names.
2635
+ */
2636
+ getRegisteredTypes() {
2637
+ return Array.from(this._typeRegistry.keys());
2638
+ }
2639
+ /**
2640
+ * Get the custom type assigned to a key.
2641
+ *
2642
+ * @param key - Key to check
2643
+ * @returns Type name or undefined if no custom type assigned
2644
+ */
2645
+ getKeyType(key) {
2646
+ const attrs = this.get_attributes(key);
2647
+ return attrs?.customType;
2648
+ }
2356
2649
  /**
2357
2650
  * Helper to get sorted keys (by zIndex).
2358
2651
  * Respects context filtering when set.
@@ -2614,9 +2907,28 @@ var MindCache = class {
2614
2907
  findKeyFromSanitizedTool(sanitizedKey) {
2615
2908
  return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
2616
2909
  }
2910
+ /**
2911
+ * Generate framework-agnostic tools with raw JSON Schema.
2912
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, and other frameworks.
2913
+ *
2914
+ * Tool format:
2915
+ * {
2916
+ * description: string,
2917
+ * parameters: { type: 'object', properties: {...}, required: [...] },
2918
+ * execute: async (args) => result
2919
+ * }
2920
+ *
2921
+ * Security: All tools use llm_set_key internally which:
2922
+ * - Only modifies VALUES, never attributes/systemTags
2923
+ * - Prevents LLMs from escalating privileges
2924
+ */
2925
+ create_tools() {
2926
+ return AIToolBuilder.createTools(this);
2927
+ }
2617
2928
  /**
2618
2929
  * Generate Vercel AI SDK compatible tools for writable keys.
2619
- * For document type keys, generates additional tools: append_, insert_, edit_
2930
+ * Wraps parameters with jsonSchema() for AI SDK v5 compatibility.
2931
+ * Use this with: generateText(), streamText() from 'ai' package.
2620
2932
  *
2621
2933
  * Security: All tools use llm_set_key internally which:
2622
2934
  * - Only modifies VALUES, never attributes/systemTags