mindcache 3.5.2 → 3.6.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.
@@ -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;
@@ -478,6 +442,76 @@ var DEFAULT_KEY_ATTRIBUTES = {
478
442
  zIndex: 0
479
443
  };
480
444
 
445
+ // src/core/SchemaParser.ts
446
+ var SchemaParser = class {
447
+ /**
448
+ * Parse a markdown schema string into a CustomTypeDefinition
449
+ * @param schema - Markdown schema string
450
+ * @returns Parsed type definition
451
+ * @throws Error if schema format is invalid
452
+ */
453
+ static parse(schema) {
454
+ const lines = schema.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
455
+ if (lines.length === 0) {
456
+ throw new Error("Schema cannot be empty");
457
+ }
458
+ const typeNameMatch = lines[0].match(/^#\s*(\w+)$/);
459
+ if (!typeNameMatch) {
460
+ throw new Error(`Invalid schema: first line must be "#TypeName", got "${lines[0]}"`);
461
+ }
462
+ const typeName = typeNameMatch[1];
463
+ const fields = [];
464
+ for (let i = 1; i < lines.length; i++) {
465
+ const line = lines[i];
466
+ const fieldMatch = line.match(/^\*\s*([^:]+):\s*(.+)$/);
467
+ if (fieldMatch) {
468
+ fields.push({
469
+ name: fieldMatch[1].trim(),
470
+ description: fieldMatch[2].trim()
471
+ });
472
+ }
473
+ }
474
+ if (fields.length === 0) {
475
+ throw new Error(`Schema "${typeName}" must have at least one field`);
476
+ }
477
+ return {
478
+ name: typeName,
479
+ fields,
480
+ rawSchema: schema
481
+ };
482
+ }
483
+ /**
484
+ * Generate a markdown representation of a type definition
485
+ * Useful for including in LLM prompts
486
+ */
487
+ static toMarkdown(typeDef) {
488
+ const lines = [`#${typeDef.name}`];
489
+ for (const field of typeDef.fields) {
490
+ lines.push(`* ${field.name}: ${field.description}`);
491
+ }
492
+ return lines.join("\n");
493
+ }
494
+ /**
495
+ * Generate a prompt-friendly description of the type
496
+ * More verbose than toMarkdown, better for LLM guidance
497
+ */
498
+ static toPromptDescription(typeDef) {
499
+ const fieldDescs = typeDef.fields.map((f) => ` - ${f.name}: ${f.description}`).join("\n");
500
+ return `Type "${typeDef.name}" with fields:
501
+ ${fieldDescs}`;
502
+ }
503
+ /**
504
+ * Generate an example value structure based on the type definition
505
+ */
506
+ static generateExample(typeDef) {
507
+ const lines = [`#${typeDef.name.toLowerCase()}`];
508
+ for (const field of typeDef.fields) {
509
+ lines.push(`* ${field.name}: [${field.description}]`);
510
+ }
511
+ return lines.join("\n");
512
+ }
513
+ };
514
+
481
515
  // src/core/MarkdownSerializer.ts
482
516
  var MarkdownSerializer = class {
483
517
  /**
@@ -824,18 +858,16 @@ var MarkdownSerializer = class {
824
858
  return result;
825
859
  }
826
860
  };
827
-
828
- // src/core/AIToolBuilder.ts
829
861
  var AIToolBuilder = class _AIToolBuilder {
830
862
  /**
831
- * Sanitize key name for use in tool names
832
- */
863
+ * Sanitize key name for use in tool names
864
+ */
833
865
  static sanitizeKeyForTool(key) {
834
866
  return key.replace(/[^a-zA-Z0-9_-]/g, "_");
835
867
  }
836
868
  /**
837
- * Find original key from sanitized tool name
838
- */
869
+ * Find original key from sanitized tool name
870
+ */
839
871
  static findKeyFromSanitizedTool(mc, sanitizedKey) {
840
872
  for (const key of mc.keys()) {
841
873
  if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
@@ -845,15 +877,174 @@ var AIToolBuilder = class _AIToolBuilder {
845
877
  return void 0;
846
878
  }
847
879
  /**
848
- * Generate Vercel AI SDK compatible tools for writable keys.
849
- * For document type keys, generates additional tools: append_, insert_, edit_
850
- *
851
- * Security: All tools use llm_set_key internally which:
852
- * - Only modifies VALUES, never attributes/systemTags
853
- * - Prevents LLMs from escalating privileges
854
- */
880
+ * Generate framework-agnostic tools with raw JSON Schema.
881
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, etc.
882
+ *
883
+ * Tool format:
884
+ * {
885
+ * description: string,
886
+ * parameters: { type: 'object', properties: {...}, required: [...] },
887
+ * execute: async (args) => result
888
+ * }
889
+ */
890
+ static createTools(mc) {
891
+ return _AIToolBuilder._buildTools(mc);
892
+ }
893
+ /**
894
+ * Generate Vercel AI SDK v5 compatible tools using Zod schemas.
895
+ * Uses tool() helper with Zod for full AI SDK v5 compatibility.
896
+ *
897
+ * Use this with: generateText(), streamText() from 'ai' package
898
+ */
855
899
  static createVercelAITools(mc) {
856
900
  const tools = {};
901
+ const registeredTypes = mc.getRegisteredTypes();
902
+ const typeDesc = registeredTypes.length > 0 ? `Optional type: ${registeredTypes.join(" | ")}` : "No types registered";
903
+ tools["create_key"] = tool({
904
+ description: `Create a new key in MindCache. ${registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : ""}`,
905
+ inputSchema: z.object({
906
+ key: z.string().describe('The key name (e.g., "contact_john_doe")'),
907
+ value: z.string().describe("The value (JSON string for structured data)"),
908
+ type: z.string().optional().describe(typeDesc)
909
+ }),
910
+ execute: async ({ key, value, type }) => {
911
+ if (mc.has(key)) {
912
+ return { result: `Key "${key}" exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)}`, error: true };
913
+ }
914
+ if (type && !mc.getTypeSchema(type)) {
915
+ return { result: `Type "${type}" not registered`, error: true };
916
+ }
917
+ mc.set_value(key, value, { systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"] });
918
+ if (type) {
919
+ mc.setType(key, type);
920
+ }
921
+ return { result: `Created "${key}"${type ? ` (${type})` : ""}`, key, value };
922
+ }
923
+ });
924
+ for (const key of mc.keys()) {
925
+ if (key.startsWith("$") || !mc.keyMatchesContext(key)) {
926
+ continue;
927
+ }
928
+ const attrs = mc.get_attributes(key);
929
+ if (!attrs?.systemTags?.includes("LLMWrite")) {
930
+ continue;
931
+ }
932
+ const sanitized = _AIToolBuilder.sanitizeKeyForTool(key);
933
+ const customTypeName = mc.getKeyType(key);
934
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
935
+ let desc = `Write to "${key}"`;
936
+ if (customType) {
937
+ desc = `Write to "${key}" (${customTypeName}). ${SchemaParser.toPromptDescription(customType)}`;
938
+ }
939
+ tools[`write_${sanitized}`] = tool({
940
+ description: desc,
941
+ inputSchema: z.object({
942
+ value: z.string().describe(customType ? `JSON following ${customTypeName} schema` : "Value to write")
943
+ }),
944
+ execute: async ({ value }) => {
945
+ const success = mc.llm_set_key(key, value);
946
+ return success ? { result: `Wrote to ${key}`, key, value } : { result: `Failed to write to ${key}`, error: true };
947
+ }
948
+ });
949
+ if (attrs?.type === "document") {
950
+ tools[`append_${sanitized}`] = tool({
951
+ description: `Append to "${key}" document`,
952
+ inputSchema: z.object({ text: z.string().describe("Text to append") }),
953
+ execute: async ({ text }) => {
954
+ const yText = mc.get_document(key);
955
+ if (yText) {
956
+ yText.insert(yText.length, text);
957
+ return { result: "Appended", key };
958
+ }
959
+ return { result: "Not found", error: true };
960
+ }
961
+ });
962
+ tools[`insert_${sanitized}`] = tool({
963
+ description: `Insert text at position in "${key}" document`,
964
+ inputSchema: z.object({
965
+ index: z.number().describe("Position (0 = start)"),
966
+ text: z.string().describe("Text to insert")
967
+ }),
968
+ execute: async ({ index, text }) => {
969
+ mc.insert_text(key, index, text);
970
+ return { result: `Inserted at ${index}`, key };
971
+ }
972
+ });
973
+ tools[`edit_${sanitized}`] = tool({
974
+ description: `Find and replace in "${key}" document`,
975
+ inputSchema: z.object({
976
+ find: z.string().describe("Text to find"),
977
+ replace: z.string().describe("Replacement")
978
+ }),
979
+ execute: async ({ find, replace }) => {
980
+ const yText = mc.get_document(key);
981
+ if (yText) {
982
+ const text = yText.toString();
983
+ const idx = text.indexOf(find);
984
+ if (idx !== -1) {
985
+ yText.delete(idx, find.length);
986
+ yText.insert(idx, replace);
987
+ return { result: `Replaced "${find}"`, key };
988
+ }
989
+ return { result: `"${find}" not found`, error: true };
990
+ }
991
+ return { result: "Document not found", error: true };
992
+ }
993
+ });
994
+ }
995
+ }
996
+ return tools;
997
+ }
998
+ /**
999
+ * Internal: Build tools with raw JSON Schema (framework-agnostic).
1000
+ */
1001
+ static _buildTools(mc) {
1002
+ const tools = {};
1003
+ const registeredTypes = mc.getRegisteredTypes();
1004
+ const typeInfo = registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : "No custom types registered";
1005
+ tools["create_key"] = {
1006
+ description: `Create a new key in MindCache. ${typeInfo}. The new key will be readable and writable by the LLM.`,
1007
+ parameters: {
1008
+ type: "object",
1009
+ properties: {
1010
+ key: { type: "string", description: 'The key name to create (e.g., "contact_john_doe")' },
1011
+ value: { type: "string", description: "The value to store (use JSON string for structured data)" },
1012
+ type: {
1013
+ type: "string",
1014
+ description: registeredTypes.length > 0 ? `Optional: custom type name (${registeredTypes.join(" | ")})` : "Optional: custom type name (none registered)"
1015
+ }
1016
+ },
1017
+ required: ["key", "value"]
1018
+ },
1019
+ execute: async ({ key, value, type }) => {
1020
+ if (mc.has(key)) {
1021
+ return {
1022
+ result: `Key "${key}" already exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)} to update it.`,
1023
+ key,
1024
+ error: true
1025
+ };
1026
+ }
1027
+ if (type && !mc.getTypeSchema(type)) {
1028
+ return {
1029
+ result: `Type "${type}" is not registered. Available types: ${registeredTypes.join(", ") || "none"}`,
1030
+ key,
1031
+ error: true
1032
+ };
1033
+ }
1034
+ mc.set_value(key, value, {
1035
+ systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"]
1036
+ });
1037
+ if (type) {
1038
+ mc.setType(key, type);
1039
+ }
1040
+ return {
1041
+ result: `Successfully created key "${key}"${type ? ` with type "${type}"` : ""}`,
1042
+ key,
1043
+ value,
1044
+ type
1045
+ };
1046
+ }
1047
+ };
857
1048
  for (const key of mc.keys()) {
858
1049
  if (key.startsWith("$")) {
859
1050
  continue;
@@ -868,12 +1059,28 @@ var AIToolBuilder = class _AIToolBuilder {
868
1059
  }
869
1060
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
870
1061
  const isDocument = attributes?.type === "document";
1062
+ const customTypeName = mc.getKeyType(key);
1063
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1064
+ let writeDescription;
1065
+ if (customType) {
1066
+ const schemaGuidance = SchemaParser.toPromptDescription(customType);
1067
+ const example = SchemaParser.generateExample(customType);
1068
+ writeDescription = `Write a value to "${key}" that must follow this schema:
1069
+ ${schemaGuidance}
1070
+
1071
+ Example format:
1072
+ ${example}`;
1073
+ } else if (isDocument) {
1074
+ writeDescription = `Rewrite the entire "${key}" document`;
1075
+ } else {
1076
+ writeDescription = `Write a value to the STM key: ${key}`;
1077
+ }
871
1078
  tools[`write_${sanitizedKey}`] = {
872
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
873
- inputSchema: {
1079
+ description: writeDescription,
1080
+ parameters: {
874
1081
  type: "object",
875
1082
  properties: {
876
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1083
+ value: { type: "string", description: customType ? `Value following ${customTypeName} schema` : isDocument ? "New document content" : "The value to write" }
877
1084
  },
878
1085
  required: ["value"]
879
1086
  },
@@ -896,7 +1103,7 @@ var AIToolBuilder = class _AIToolBuilder {
896
1103
  if (isDocument) {
897
1104
  tools[`append_${sanitizedKey}`] = {
898
1105
  description: `Append text to the end of "${key}" document`,
899
- inputSchema: {
1106
+ parameters: {
900
1107
  type: "object",
901
1108
  properties: {
902
1109
  text: { type: "string", description: "Text to append" }
@@ -921,7 +1128,7 @@ var AIToolBuilder = class _AIToolBuilder {
921
1128
  };
922
1129
  tools[`insert_${sanitizedKey}`] = {
923
1130
  description: `Insert text at a position in "${key}" document`,
924
- inputSchema: {
1131
+ parameters: {
925
1132
  type: "object",
926
1133
  properties: {
927
1134
  index: { type: "number", description: "Position to insert at (0 = start)" },
@@ -944,7 +1151,7 @@ var AIToolBuilder = class _AIToolBuilder {
944
1151
  };
945
1152
  tools[`edit_${sanitizedKey}`] = {
946
1153
  description: `Find and replace text in "${key}" document`,
947
- inputSchema: {
1154
+ parameters: {
948
1155
  type: "object",
949
1156
  properties: {
950
1157
  find: { type: "string", description: "Text to find" },
@@ -981,11 +1188,16 @@ var AIToolBuilder = class _AIToolBuilder {
981
1188
  return tools;
982
1189
  }
983
1190
  /**
984
- * Generate a system prompt containing all visible STM keys and their values.
985
- * Indicates which tools can be used to modify writable keys.
986
- */
1191
+ * Generate a system prompt containing all visible STM keys and their values.
1192
+ * Indicates which tools can be used to modify writable keys.
1193
+ */
987
1194
  static getSystemPrompt(mc) {
988
1195
  const lines = [];
1196
+ const registeredTypes = mc.getRegisteredTypes();
1197
+ if (registeredTypes.length > 0) {
1198
+ lines.push(`[create_key tool available - registered types: ${registeredTypes.join(", ")}]`);
1199
+ lines.push("");
1200
+ }
989
1201
  for (const key of mc.keys()) {
990
1202
  if (key.startsWith("$")) {
991
1203
  continue;
@@ -1003,8 +1215,18 @@ var AIToolBuilder = class _AIToolBuilder {
1003
1215
  const isWritable = attributes?.systemTags?.includes("LLMWrite");
1004
1216
  const isDocument = attributes?.type === "document";
1005
1217
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
1218
+ const customTypeName = mc.getKeyType(key);
1219
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1006
1220
  if (isWritable) {
1007
- if (isDocument) {
1221
+ if (customType) {
1222
+ const schemaInfo = SchemaParser.toMarkdown(customType);
1223
+ lines.push(
1224
+ `${key} (type: ${customTypeName}): ${displayValue}
1225
+ Schema:
1226
+ ${schemaInfo}
1227
+ Tool: write_${sanitizedKey}`
1228
+ );
1229
+ } else if (isDocument) {
1008
1230
  lines.push(
1009
1231
  `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
1010
1232
  );
@@ -1015,15 +1237,19 @@ var AIToolBuilder = class _AIToolBuilder {
1015
1237
  );
1016
1238
  }
1017
1239
  } else {
1018
- lines.push(`${key}: ${displayValue}`);
1240
+ if (customTypeName) {
1241
+ lines.push(`${key} (type: ${customTypeName}): ${displayValue}`);
1242
+ } else {
1243
+ lines.push(`${key}: ${displayValue}`);
1244
+ }
1019
1245
  }
1020
1246
  }
1021
1247
  return lines.join("\n");
1022
1248
  }
1023
1249
  /**
1024
- * Execute a tool call by name with the given value.
1025
- * Returns the result or null if tool not found.
1026
- */
1250
+ * Execute a tool call by name with the given value.
1251
+ * Returns the result or null if tool not found.
1252
+ */
1027
1253
  static executeToolCall(mc, toolName, value) {
1028
1254
  const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
1029
1255
  if (!match) {
@@ -1350,7 +1576,7 @@ var MindCache = class {
1350
1576
  listeners = {};
1351
1577
  globalListeners = [];
1352
1578
  // Metadata
1353
- version = "3.3.2";
1579
+ version = "3.6.0";
1354
1580
  // Internal flag to prevent sync loops when receiving remote updates
1355
1581
  // (Less critical with Yjs but kept for API compat)
1356
1582
  normalizeSystemTags(tags) {
@@ -1387,6 +1613,8 @@ var MindCache = class {
1387
1613
  _history = [];
1388
1614
  _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
1389
1615
  _historyEnabled = false;
1616
+ // Custom type registry
1617
+ _typeRegistry = /* @__PURE__ */ new Map();
1390
1618
  constructor(options) {
1391
1619
  this.doc = options?.doc || new Y.Doc();
1392
1620
  this.rootMap = this.doc.getMap("mindcache");
@@ -2343,6 +2571,71 @@ var MindCache = class {
2343
2571
  systemGetKeysByTag(tag) {
2344
2572
  return TagManager.systemGetKeysByTag(this, tag);
2345
2573
  }
2574
+ // ============================================
2575
+ // Custom Type Methods
2576
+ // ============================================
2577
+ /**
2578
+ * Register a custom type with a markdown schema definition.
2579
+ *
2580
+ * Schema format:
2581
+ * ```
2582
+ * #TypeName
2583
+ * * fieldName: description of the field
2584
+ * * anotherField: description
2585
+ * ```
2586
+ *
2587
+ * @param name - Type name (e.g., 'Contact')
2588
+ * @param schema - Markdown schema definition
2589
+ * @throws Error if schema format is invalid
2590
+ */
2591
+ registerType(name, schema) {
2592
+ const typeDef = SchemaParser.parse(schema);
2593
+ typeDef.name = name;
2594
+ this._typeRegistry.set(name, typeDef);
2595
+ }
2596
+ /**
2597
+ * Assign a custom type to a key.
2598
+ * The key must exist and the type must be registered.
2599
+ * Also sets the underlying type to 'json' since custom types are structured JSON data.
2600
+ *
2601
+ * @param key - Key to assign type to
2602
+ * @param typeName - Registered type name
2603
+ * @throws Error if key doesn't exist or type is not registered
2604
+ */
2605
+ setType(key, typeName) {
2606
+ if (!this.rootMap.has(key)) {
2607
+ throw new Error(`Key "${key}" does not exist`);
2608
+ }
2609
+ if (!this._typeRegistry.has(typeName)) {
2610
+ throw new Error(`Type "${typeName}" is not registered. Use registerType() first.`);
2611
+ }
2612
+ this.set_attributes(key, { type: "json", customType: typeName });
2613
+ }
2614
+ /**
2615
+ * Get a registered type schema definition.
2616
+ *
2617
+ * @param typeName - Type name to look up
2618
+ * @returns The type definition or undefined if not registered
2619
+ */
2620
+ getTypeSchema(typeName) {
2621
+ return this._typeRegistry.get(typeName);
2622
+ }
2623
+ /**
2624
+ * Get all registered type names.
2625
+ */
2626
+ getRegisteredTypes() {
2627
+ return Array.from(this._typeRegistry.keys());
2628
+ }
2629
+ /**
2630
+ * Get the custom type assigned to a key.
2631
+ *
2632
+ * @param key - Key to check
2633
+ * @returns Type name or undefined if no custom type assigned
2634
+ */
2635
+ getKeyType(key) {
2636
+ const attrs = this.get_attributes(key);
2637
+ return attrs?.customType;
2638
+ }
2346
2639
  /**
2347
2640
  * Helper to get sorted keys (by zIndex).
2348
2641
  * Respects context filtering when set.
@@ -2604,9 +2897,28 @@ var MindCache = class {
2604
2897
  findKeyFromSanitizedTool(sanitizedKey) {
2605
2898
  return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
2606
2899
  }
2900
+ /**
2901
+ * Generate framework-agnostic tools with raw JSON Schema.
2902
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, and other frameworks.
2903
+ *
2904
+ * Tool format:
2905
+ * {
2906
+ * description: string,
2907
+ * parameters: { type: 'object', properties: {...}, required: [...] },
2908
+ * execute: async (args) => result
2909
+ * }
2910
+ *
2911
+ * Security: All tools use llm_set_key internally which:
2912
+ * - Only modifies VALUES, never attributes/systemTags
2913
+ * - Prevents LLMs from escalating privileges
2914
+ */
2915
+ create_tools() {
2916
+ return AIToolBuilder.createTools(this);
2917
+ }
2607
2918
  /**
2608
2919
  * Generate Vercel AI SDK compatible tools for writable keys.
2609
- * For document type keys, generates additional tools: append_, insert_, edit_
2920
+ * Wraps parameters with jsonSchema() for AI SDK v5 compatibility.
2921
+ * Use this with: generateText(), streamText() from 'ai' package.
2610
2922
  *
2611
2923
  * Security: All tools use llm_set_key internally which:
2612
2924
  * - Only modifies VALUES, never attributes/systemTags