mindcache 3.5.3 → 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.
package/dist/index.js CHANGED
@@ -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
  var react = require('react');
10
12
 
11
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -212,7 +214,6 @@ var init_CloudAdapter = __esm({
212
214
  if (!config.baseUrl) {
213
215
  throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
214
216
  }
215
- this.validateConfig(config);
216
217
  this.setupNetworkDetection();
217
218
  }
218
219
  ws = null;
@@ -228,43 +229,6 @@ var init_CloudAdapter = __esm({
228
229
  handleOnline = null;
229
230
  handleOffline = null;
230
231
  _synced = false;
231
- /**
232
- * Validate configuration and warn about common mistakes
233
- */
234
- validateConfig(config) {
235
- const baseUrl = config.baseUrl;
236
- if (!baseUrl) {
237
- return;
238
- }
239
- console.log("\u2601\uFE0F MindCache Cloud Config:", {
240
- baseUrl,
241
- instanceId: config.instanceId,
242
- hasTokenProvider: !!config.tokenProvider,
243
- hasApiKey: !!config.apiKey
244
- });
245
- try {
246
- const url = new URL(baseUrl);
247
- if (url.hostname === "mindcache.dev") {
248
- console.error(
249
- '\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)."
250
- );
251
- }
252
- if (url.protocol === "ws:" || url.protocol === "wss:") {
253
- console.warn(
254
- "\u26A0\uFE0F MindCache Cloud: baseUrl uses WebSocket protocol (" + url.protocol + "). Consider using http:// or https:// - CloudAdapter will handle the WebSocket upgrade automatically."
255
- );
256
- }
257
- if (url.hostname === "localhost" && url.port !== "8787" && url.port !== "3000") {
258
- console.warn(
259
- "\u26A0\uFE0F MindCache Cloud: localhost URL detected with non-standard port " + url.port + ". Default MindCache dev server runs on port 8787."
260
- );
261
- }
262
- const wsUrl = baseUrl.replace("https://", "wss://").replace("http://", "ws://");
263
- console.log("\u2601\uFE0F WebSocket will connect to:", wsUrl + "/sync/" + config.instanceId);
264
- } catch (e) {
265
- console.error("\u26A0\uFE0F MindCache Cloud: Invalid baseUrl format:", baseUrl);
266
- }
267
- }
268
232
  /** Browser network status - instantly updated via navigator.onLine */
269
233
  get isOnline() {
270
234
  return this._isOnline;
@@ -517,6 +481,76 @@ var SystemTagHelpers = {
517
481
  hasTemplateInjection: (attrs) => attrs.systemTags.includes("ApplyTemplate")
518
482
  };
519
483
 
484
+ // src/core/SchemaParser.ts
485
+ var SchemaParser = class {
486
+ /**
487
+ * Parse a markdown schema string into a CustomTypeDefinition
488
+ * @param schema - Markdown schema string
489
+ * @returns Parsed type definition
490
+ * @throws Error if schema format is invalid
491
+ */
492
+ static parse(schema) {
493
+ const lines = schema.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
494
+ if (lines.length === 0) {
495
+ throw new Error("Schema cannot be empty");
496
+ }
497
+ const typeNameMatch = lines[0].match(/^#\s*(\w+)$/);
498
+ if (!typeNameMatch) {
499
+ throw new Error(`Invalid schema: first line must be "#TypeName", got "${lines[0]}"`);
500
+ }
501
+ const typeName = typeNameMatch[1];
502
+ const fields = [];
503
+ for (let i = 1; i < lines.length; i++) {
504
+ const line = lines[i];
505
+ const fieldMatch = line.match(/^\*\s*([^:]+):\s*(.+)$/);
506
+ if (fieldMatch) {
507
+ fields.push({
508
+ name: fieldMatch[1].trim(),
509
+ description: fieldMatch[2].trim()
510
+ });
511
+ }
512
+ }
513
+ if (fields.length === 0) {
514
+ throw new Error(`Schema "${typeName}" must have at least one field`);
515
+ }
516
+ return {
517
+ name: typeName,
518
+ fields,
519
+ rawSchema: schema
520
+ };
521
+ }
522
+ /**
523
+ * Generate a markdown representation of a type definition
524
+ * Useful for including in LLM prompts
525
+ */
526
+ static toMarkdown(typeDef) {
527
+ const lines = [`#${typeDef.name}`];
528
+ for (const field of typeDef.fields) {
529
+ lines.push(`* ${field.name}: ${field.description}`);
530
+ }
531
+ return lines.join("\n");
532
+ }
533
+ /**
534
+ * Generate a prompt-friendly description of the type
535
+ * More verbose than toMarkdown, better for LLM guidance
536
+ */
537
+ static toPromptDescription(typeDef) {
538
+ const fieldDescs = typeDef.fields.map((f) => ` - ${f.name}: ${f.description}`).join("\n");
539
+ return `Type "${typeDef.name}" with fields:
540
+ ${fieldDescs}`;
541
+ }
542
+ /**
543
+ * Generate an example value structure based on the type definition
544
+ */
545
+ static generateExample(typeDef) {
546
+ const lines = [`#${typeDef.name.toLowerCase()}`];
547
+ for (const field of typeDef.fields) {
548
+ lines.push(`* ${field.name}: [${field.description}]`);
549
+ }
550
+ return lines.join("\n");
551
+ }
552
+ };
553
+
520
554
  // src/core/MarkdownSerializer.ts
521
555
  var MarkdownSerializer = class {
522
556
  /**
@@ -863,18 +897,16 @@ var MarkdownSerializer = class {
863
897
  return result;
864
898
  }
865
899
  };
866
-
867
- // src/core/AIToolBuilder.ts
868
900
  var AIToolBuilder = class _AIToolBuilder {
869
901
  /**
870
- * Sanitize key name for use in tool names
871
- */
902
+ * Sanitize key name for use in tool names
903
+ */
872
904
  static sanitizeKeyForTool(key) {
873
905
  return key.replace(/[^a-zA-Z0-9_-]/g, "_");
874
906
  }
875
907
  /**
876
- * Find original key from sanitized tool name
877
- */
908
+ * Find original key from sanitized tool name
909
+ */
878
910
  static findKeyFromSanitizedTool(mc, sanitizedKey) {
879
911
  for (const key of mc.keys()) {
880
912
  if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
@@ -884,15 +916,174 @@ var AIToolBuilder = class _AIToolBuilder {
884
916
  return void 0;
885
917
  }
886
918
  /**
887
- * Generate Vercel AI SDK compatible tools for writable keys.
888
- * For document type keys, generates additional tools: append_, insert_, edit_
889
- *
890
- * Security: All tools use llm_set_key internally which:
891
- * - Only modifies VALUES, never attributes/systemTags
892
- * - Prevents LLMs from escalating privileges
893
- */
919
+ * Generate framework-agnostic tools with raw JSON Schema.
920
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, etc.
921
+ *
922
+ * Tool format:
923
+ * {
924
+ * description: string,
925
+ * parameters: { type: 'object', properties: {...}, required: [...] },
926
+ * execute: async (args) => result
927
+ * }
928
+ */
929
+ static createTools(mc) {
930
+ return _AIToolBuilder._buildTools(mc);
931
+ }
932
+ /**
933
+ * Generate Vercel AI SDK v5 compatible tools using Zod schemas.
934
+ * Uses tool() helper with Zod for full AI SDK v5 compatibility.
935
+ *
936
+ * Use this with: generateText(), streamText() from 'ai' package
937
+ */
894
938
  static createVercelAITools(mc) {
895
939
  const tools = {};
940
+ const registeredTypes = mc.getRegisteredTypes();
941
+ const typeDesc = registeredTypes.length > 0 ? `Optional type: ${registeredTypes.join(" | ")}` : "No types registered";
942
+ tools["create_key"] = ai.tool({
943
+ description: `Create a new key in MindCache. ${registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : ""}`,
944
+ inputSchema: zod.z.object({
945
+ key: zod.z.string().describe('The key name (e.g., "contact_john_doe")'),
946
+ value: zod.z.string().describe("The value (JSON string for structured data)"),
947
+ type: zod.z.string().optional().describe(typeDesc)
948
+ }),
949
+ execute: async ({ key, value, type }) => {
950
+ if (mc.has(key)) {
951
+ return { result: `Key "${key}" exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)}`, error: true };
952
+ }
953
+ if (type && !mc.getTypeSchema(type)) {
954
+ return { result: `Type "${type}" not registered`, error: true };
955
+ }
956
+ mc.set_value(key, value, { systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"] });
957
+ if (type) {
958
+ mc.setType(key, type);
959
+ }
960
+ return { result: `Created "${key}"${type ? ` (${type})` : ""}`, key, value };
961
+ }
962
+ });
963
+ for (const key of mc.keys()) {
964
+ if (key.startsWith("$") || !mc.keyMatchesContext(key)) {
965
+ continue;
966
+ }
967
+ const attrs = mc.get_attributes(key);
968
+ if (!attrs?.systemTags?.includes("LLMWrite")) {
969
+ continue;
970
+ }
971
+ const sanitized = _AIToolBuilder.sanitizeKeyForTool(key);
972
+ const customTypeName = mc.getKeyType(key);
973
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
974
+ let desc = `Write to "${key}"`;
975
+ if (customType) {
976
+ desc = `Write to "${key}" (${customTypeName}). ${SchemaParser.toPromptDescription(customType)}`;
977
+ }
978
+ tools[`write_${sanitized}`] = ai.tool({
979
+ description: desc,
980
+ inputSchema: zod.z.object({
981
+ value: zod.z.string().describe(customType ? `JSON following ${customTypeName} schema` : "Value to write")
982
+ }),
983
+ execute: async ({ value }) => {
984
+ const success = mc.llm_set_key(key, value);
985
+ return success ? { result: `Wrote to ${key}`, key, value } : { result: `Failed to write to ${key}`, error: true };
986
+ }
987
+ });
988
+ if (attrs?.type === "document") {
989
+ tools[`append_${sanitized}`] = ai.tool({
990
+ description: `Append to "${key}" document`,
991
+ inputSchema: zod.z.object({ text: zod.z.string().describe("Text to append") }),
992
+ execute: async ({ text }) => {
993
+ const yText = mc.get_document(key);
994
+ if (yText) {
995
+ yText.insert(yText.length, text);
996
+ return { result: "Appended", key };
997
+ }
998
+ return { result: "Not found", error: true };
999
+ }
1000
+ });
1001
+ tools[`insert_${sanitized}`] = ai.tool({
1002
+ description: `Insert text at position in "${key}" document`,
1003
+ inputSchema: zod.z.object({
1004
+ index: zod.z.number().describe("Position (0 = start)"),
1005
+ text: zod.z.string().describe("Text to insert")
1006
+ }),
1007
+ execute: async ({ index, text }) => {
1008
+ mc.insert_text(key, index, text);
1009
+ return { result: `Inserted at ${index}`, key };
1010
+ }
1011
+ });
1012
+ tools[`edit_${sanitized}`] = ai.tool({
1013
+ description: `Find and replace in "${key}" document`,
1014
+ inputSchema: zod.z.object({
1015
+ find: zod.z.string().describe("Text to find"),
1016
+ replace: zod.z.string().describe("Replacement")
1017
+ }),
1018
+ execute: async ({ find, replace }) => {
1019
+ const yText = mc.get_document(key);
1020
+ if (yText) {
1021
+ const text = yText.toString();
1022
+ const idx = text.indexOf(find);
1023
+ if (idx !== -1) {
1024
+ yText.delete(idx, find.length);
1025
+ yText.insert(idx, replace);
1026
+ return { result: `Replaced "${find}"`, key };
1027
+ }
1028
+ return { result: `"${find}" not found`, error: true };
1029
+ }
1030
+ return { result: "Document not found", error: true };
1031
+ }
1032
+ });
1033
+ }
1034
+ }
1035
+ return tools;
1036
+ }
1037
+ /**
1038
+ * Internal: Build tools with raw JSON Schema (framework-agnostic).
1039
+ */
1040
+ static _buildTools(mc) {
1041
+ const tools = {};
1042
+ const registeredTypes = mc.getRegisteredTypes();
1043
+ const typeInfo = registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : "No custom types registered";
1044
+ tools["create_key"] = {
1045
+ description: `Create a new key in MindCache. ${typeInfo}. The new key will be readable and writable by the LLM.`,
1046
+ parameters: {
1047
+ type: "object",
1048
+ properties: {
1049
+ key: { type: "string", description: 'The key name to create (e.g., "contact_john_doe")' },
1050
+ value: { type: "string", description: "The value to store (use JSON string for structured data)" },
1051
+ type: {
1052
+ type: "string",
1053
+ description: registeredTypes.length > 0 ? `Optional: custom type name (${registeredTypes.join(" | ")})` : "Optional: custom type name (none registered)"
1054
+ }
1055
+ },
1056
+ required: ["key", "value"]
1057
+ },
1058
+ execute: async ({ key, value, type }) => {
1059
+ if (mc.has(key)) {
1060
+ return {
1061
+ result: `Key "${key}" already exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)} to update it.`,
1062
+ key,
1063
+ error: true
1064
+ };
1065
+ }
1066
+ if (type && !mc.getTypeSchema(type)) {
1067
+ return {
1068
+ result: `Type "${type}" is not registered. Available types: ${registeredTypes.join(", ") || "none"}`,
1069
+ key,
1070
+ error: true
1071
+ };
1072
+ }
1073
+ mc.set_value(key, value, {
1074
+ systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"]
1075
+ });
1076
+ if (type) {
1077
+ mc.setType(key, type);
1078
+ }
1079
+ return {
1080
+ result: `Successfully created key "${key}"${type ? ` with type "${type}"` : ""}`,
1081
+ key,
1082
+ value,
1083
+ type
1084
+ };
1085
+ }
1086
+ };
896
1087
  for (const key of mc.keys()) {
897
1088
  if (key.startsWith("$")) {
898
1089
  continue;
@@ -907,12 +1098,28 @@ var AIToolBuilder = class _AIToolBuilder {
907
1098
  }
908
1099
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
909
1100
  const isDocument = attributes?.type === "document";
1101
+ const customTypeName = mc.getKeyType(key);
1102
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1103
+ let writeDescription;
1104
+ if (customType) {
1105
+ const schemaGuidance = SchemaParser.toPromptDescription(customType);
1106
+ const example = SchemaParser.generateExample(customType);
1107
+ writeDescription = `Write a value to "${key}" that must follow this schema:
1108
+ ${schemaGuidance}
1109
+
1110
+ Example format:
1111
+ ${example}`;
1112
+ } else if (isDocument) {
1113
+ writeDescription = `Rewrite the entire "${key}" document`;
1114
+ } else {
1115
+ writeDescription = `Write a value to the STM key: ${key}`;
1116
+ }
910
1117
  tools[`write_${sanitizedKey}`] = {
911
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
912
- inputSchema: {
1118
+ description: writeDescription,
1119
+ parameters: {
913
1120
  type: "object",
914
1121
  properties: {
915
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1122
+ value: { type: "string", description: customType ? `Value following ${customTypeName} schema` : isDocument ? "New document content" : "The value to write" }
916
1123
  },
917
1124
  required: ["value"]
918
1125
  },
@@ -935,7 +1142,7 @@ var AIToolBuilder = class _AIToolBuilder {
935
1142
  if (isDocument) {
936
1143
  tools[`append_${sanitizedKey}`] = {
937
1144
  description: `Append text to the end of "${key}" document`,
938
- inputSchema: {
1145
+ parameters: {
939
1146
  type: "object",
940
1147
  properties: {
941
1148
  text: { type: "string", description: "Text to append" }
@@ -960,7 +1167,7 @@ var AIToolBuilder = class _AIToolBuilder {
960
1167
  };
961
1168
  tools[`insert_${sanitizedKey}`] = {
962
1169
  description: `Insert text at a position in "${key}" document`,
963
- inputSchema: {
1170
+ parameters: {
964
1171
  type: "object",
965
1172
  properties: {
966
1173
  index: { type: "number", description: "Position to insert at (0 = start)" },
@@ -983,7 +1190,7 @@ var AIToolBuilder = class _AIToolBuilder {
983
1190
  };
984
1191
  tools[`edit_${sanitizedKey}`] = {
985
1192
  description: `Find and replace text in "${key}" document`,
986
- inputSchema: {
1193
+ parameters: {
987
1194
  type: "object",
988
1195
  properties: {
989
1196
  find: { type: "string", description: "Text to find" },
@@ -1020,11 +1227,16 @@ var AIToolBuilder = class _AIToolBuilder {
1020
1227
  return tools;
1021
1228
  }
1022
1229
  /**
1023
- * Generate a system prompt containing all visible STM keys and their values.
1024
- * Indicates which tools can be used to modify writable keys.
1025
- */
1230
+ * Generate a system prompt containing all visible STM keys and their values.
1231
+ * Indicates which tools can be used to modify writable keys.
1232
+ */
1026
1233
  static getSystemPrompt(mc) {
1027
1234
  const lines = [];
1235
+ const registeredTypes = mc.getRegisteredTypes();
1236
+ if (registeredTypes.length > 0) {
1237
+ lines.push(`[create_key tool available - registered types: ${registeredTypes.join(", ")}]`);
1238
+ lines.push("");
1239
+ }
1028
1240
  for (const key of mc.keys()) {
1029
1241
  if (key.startsWith("$")) {
1030
1242
  continue;
@@ -1042,8 +1254,18 @@ var AIToolBuilder = class _AIToolBuilder {
1042
1254
  const isWritable = attributes?.systemTags?.includes("LLMWrite");
1043
1255
  const isDocument = attributes?.type === "document";
1044
1256
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
1257
+ const customTypeName = mc.getKeyType(key);
1258
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1045
1259
  if (isWritable) {
1046
- if (isDocument) {
1260
+ if (customType) {
1261
+ const schemaInfo = SchemaParser.toMarkdown(customType);
1262
+ lines.push(
1263
+ `${key} (type: ${customTypeName}): ${displayValue}
1264
+ Schema:
1265
+ ${schemaInfo}
1266
+ Tool: write_${sanitizedKey}`
1267
+ );
1268
+ } else if (isDocument) {
1047
1269
  lines.push(
1048
1270
  `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
1049
1271
  );
@@ -1054,15 +1276,19 @@ var AIToolBuilder = class _AIToolBuilder {
1054
1276
  );
1055
1277
  }
1056
1278
  } else {
1057
- lines.push(`${key}: ${displayValue}`);
1279
+ if (customTypeName) {
1280
+ lines.push(`${key} (type: ${customTypeName}): ${displayValue}`);
1281
+ } else {
1282
+ lines.push(`${key}: ${displayValue}`);
1283
+ }
1058
1284
  }
1059
1285
  }
1060
1286
  return lines.join("\n");
1061
1287
  }
1062
1288
  /**
1063
- * Execute a tool call by name with the given value.
1064
- * Returns the result or null if tool not found.
1065
- */
1289
+ * Execute a tool call by name with the given value.
1290
+ * Returns the result or null if tool not found.
1291
+ */
1066
1292
  static executeToolCall(mc, toolName, value) {
1067
1293
  const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
1068
1294
  if (!match) {
@@ -1389,7 +1615,7 @@ var MindCache = class {
1389
1615
  listeners = {};
1390
1616
  globalListeners = [];
1391
1617
  // Metadata
1392
- version = "3.3.2";
1618
+ version = "3.6.0";
1393
1619
  // Internal flag to prevent sync loops when receiving remote updates
1394
1620
  // (Less critical with Yjs but kept for API compat)
1395
1621
  normalizeSystemTags(tags) {
@@ -1426,6 +1652,8 @@ var MindCache = class {
1426
1652
  _history = [];
1427
1653
  _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
1428
1654
  _historyEnabled = false;
1655
+ // Custom type registry
1656
+ _typeRegistry = /* @__PURE__ */ new Map();
1429
1657
  constructor(options) {
1430
1658
  this.doc = options?.doc || new Y__namespace.Doc();
1431
1659
  this.rootMap = this.doc.getMap("mindcache");
@@ -2382,6 +2610,71 @@ var MindCache = class {
2382
2610
  systemGetKeysByTag(tag) {
2383
2611
  return TagManager.systemGetKeysByTag(this, tag);
2384
2612
  }
2613
+ // ============================================
2614
+ // Custom Type Methods
2615
+ // ============================================
2616
+ /**
2617
+ * Register a custom type with a markdown schema definition.
2618
+ *
2619
+ * Schema format:
2620
+ * ```
2621
+ * #TypeName
2622
+ * * fieldName: description of the field
2623
+ * * anotherField: description
2624
+ * ```
2625
+ *
2626
+ * @param name - Type name (e.g., 'Contact')
2627
+ * @param schema - Markdown schema definition
2628
+ * @throws Error if schema format is invalid
2629
+ */
2630
+ registerType(name, schema) {
2631
+ const typeDef = SchemaParser.parse(schema);
2632
+ typeDef.name = name;
2633
+ this._typeRegistry.set(name, typeDef);
2634
+ }
2635
+ /**
2636
+ * Assign a custom type to a key.
2637
+ * The key must exist and the type must be registered.
2638
+ * Also sets the underlying type to 'json' since custom types are structured JSON data.
2639
+ *
2640
+ * @param key - Key to assign type to
2641
+ * @param typeName - Registered type name
2642
+ * @throws Error if key doesn't exist or type is not registered
2643
+ */
2644
+ setType(key, typeName) {
2645
+ if (!this.rootMap.has(key)) {
2646
+ throw new Error(`Key "${key}" does not exist`);
2647
+ }
2648
+ if (!this._typeRegistry.has(typeName)) {
2649
+ throw new Error(`Type "${typeName}" is not registered. Use registerType() first.`);
2650
+ }
2651
+ this.set_attributes(key, { type: "json", customType: typeName });
2652
+ }
2653
+ /**
2654
+ * Get a registered type schema definition.
2655
+ *
2656
+ * @param typeName - Type name to look up
2657
+ * @returns The type definition or undefined if not registered
2658
+ */
2659
+ getTypeSchema(typeName) {
2660
+ return this._typeRegistry.get(typeName);
2661
+ }
2662
+ /**
2663
+ * Get all registered type names.
2664
+ */
2665
+ getRegisteredTypes() {
2666
+ return Array.from(this._typeRegistry.keys());
2667
+ }
2668
+ /**
2669
+ * Get the custom type assigned to a key.
2670
+ *
2671
+ * @param key - Key to check
2672
+ * @returns Type name or undefined if no custom type assigned
2673
+ */
2674
+ getKeyType(key) {
2675
+ const attrs = this.get_attributes(key);
2676
+ return attrs?.customType;
2677
+ }
2385
2678
  /**
2386
2679
  * Helper to get sorted keys (by zIndex).
2387
2680
  * Respects context filtering when set.
@@ -2643,9 +2936,28 @@ var MindCache = class {
2643
2936
  findKeyFromSanitizedTool(sanitizedKey) {
2644
2937
  return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
2645
2938
  }
2939
+ /**
2940
+ * Generate framework-agnostic tools with raw JSON Schema.
2941
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, and other frameworks.
2942
+ *
2943
+ * Tool format:
2944
+ * {
2945
+ * description: string,
2946
+ * parameters: { type: 'object', properties: {...}, required: [...] },
2947
+ * execute: async (args) => result
2948
+ * }
2949
+ *
2950
+ * Security: All tools use llm_set_key internally which:
2951
+ * - Only modifies VALUES, never attributes/systemTags
2952
+ * - Prevents LLMs from escalating privileges
2953
+ */
2954
+ create_tools() {
2955
+ return AIToolBuilder.createTools(this);
2956
+ }
2646
2957
  /**
2647
2958
  * Generate Vercel AI SDK compatible tools for writable keys.
2648
- * For document type keys, generates additional tools: append_, insert_, edit_
2959
+ * Wraps parameters with jsonSchema() for AI SDK v5 compatibility.
2960
+ * Use this with: generateText(), streamText() from 'ai' package.
2649
2961
  *
2650
2962
  * Security: All tools use llm_set_key internally which:
2651
2963
  * - Only modifies VALUES, never attributes/systemTags
@@ -2688,8 +3000,6 @@ init_CloudAdapter();
2688
3000
  init_CloudAdapter();
2689
3001
 
2690
3002
  // src/cloud/OAuthClient.ts
2691
- var DEFAULT_API_URL = "https://api.mindcache.dev";
2692
- var DEFAULT_USERINFO_URL = DEFAULT_API_URL + "/oauth/userinfo";
2693
3003
  var TOKEN_REFRESH_BUFFER = 5 * 60 * 1e3;
2694
3004
  function generateRandomString(length2) {
2695
3005
  const array = new Uint8Array(length2);
@@ -2718,6 +3028,21 @@ var OAuthClient = class {
2718
3028
  tokens = null;
2719
3029
  refreshPromise = null;
2720
3030
  constructor(config) {
3031
+ if (!config.baseUrl) {
3032
+ throw new Error(
3033
+ 'MindCache OAuth: baseUrl is required!\n For production: baseUrl: "https://api.mindcache.dev"\n For local dev: baseUrl: "http://localhost:8787"'
3034
+ );
3035
+ }
3036
+ try {
3037
+ const url = new URL(config.baseUrl);
3038
+ if (url.hostname === "mindcache.dev") {
3039
+ console.error(
3040
+ '\u274C MindCache OAuth ERROR: baseUrl should be "api.mindcache.dev" not "mindcache.dev"\n Current: ' + config.baseUrl + "\n Correct: https://api.mindcache.dev"
3041
+ );
3042
+ }
3043
+ } catch {
3044
+ throw new Error("MindCache OAuth: Invalid baseUrl format: " + config.baseUrl);
3045
+ }
2721
3046
  let redirectUri = config.redirectUri;
2722
3047
  if (!redirectUri && typeof window !== "undefined") {
2723
3048
  const url = new URL(window.location.href);
@@ -2725,55 +3050,54 @@ var OAuthClient = class {
2725
3050
  url.hash = "";
2726
3051
  redirectUri = url.toString();
2727
3052
  }
2728
- const baseUrl = config.baseUrl || config.apiUrl || DEFAULT_API_URL;
2729
- const authUrl = config.authUrl || baseUrl + "/oauth/authorize";
2730
- const tokenUrl = config.tokenUrl || baseUrl + "/oauth/token";
2731
- const apiUrl = config.apiUrl || baseUrl;
3053
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
2732
3054
  this.config = {
2733
3055
  clientId: config.clientId,
3056
+ baseUrl,
2734
3057
  redirectUri: redirectUri || "",
2735
3058
  scopes: config.scopes || ["read", "write"],
2736
- baseUrl,
2737
- authUrl,
2738
- tokenUrl,
2739
- apiUrl,
2740
3059
  usePKCE: config.usePKCE !== false,
2741
3060
  // Default true
2742
3061
  storagePrefix: config.storagePrefix || "mindcache_oauth"
2743
3062
  };
2744
- console.log("\u{1F510} MindCache OAuth Config:", {
3063
+ console.log("\u{1F510} MindCache OAuth:", {
2745
3064
  baseUrl: this.config.baseUrl,
2746
- authUrl: this.config.authUrl,
2747
- tokenUrl: this.config.tokenUrl,
2748
- apiUrl: this.config.apiUrl,
3065
+ authUrl: this.authUrl,
3066
+ tokenUrl: this.tokenUrl,
2749
3067
  clientId: this.config.clientId.substring(0, 20) + "..."
2750
3068
  });
2751
3069
  this.validateApi();
2752
3070
  this.loadTokens();
2753
3071
  }
3072
+ /** Derived auth URL */
3073
+ get authUrl() {
3074
+ return this.config.baseUrl + "/oauth/authorize";
3075
+ }
3076
+ /** Derived token URL */
3077
+ get tokenUrl() {
3078
+ return this.config.baseUrl + "/oauth/token";
3079
+ }
3080
+ /** Derived userinfo URL */
3081
+ get userinfoUrl() {
3082
+ return this.config.baseUrl + "/oauth/userinfo";
3083
+ }
2754
3084
  /**
2755
- * Validate the API is reachable and warn about common misconfigurations
3085
+ * Validate the API is reachable
2756
3086
  */
2757
3087
  async validateApi() {
2758
3088
  try {
2759
- const response = await fetch(`${this.config.apiUrl}/oauth/apps/info`, {
3089
+ const response = await fetch(`${this.config.baseUrl}/oauth/apps/info`, {
2760
3090
  method: "GET",
2761
3091
  headers: { "Accept": "application/json" }
2762
3092
  });
2763
3093
  if (response.status === 404) {
2764
3094
  console.error(
2765
- "\u274C MindCache OAuth ERROR: API not found at " + this.config.apiUrl + '\n The server returned 404. Common causes:\n - Wrong domain: Use "api.mindcache.dev" not "mindcache.dev"\n - Wrong port: Local dev server is usually on port 8787\n - Server not running: Make sure the MindCache server is started'
3095
+ "\u274C MindCache OAuth ERROR: API not found at " + this.config.baseUrl + '\n The server returned 404. Common causes:\n - Wrong domain: Use "api.mindcache.dev" not "mindcache.dev"\n - Wrong port: Local dev server is usually on port 8787\n - Server not running: Make sure the MindCache server is started'
2766
3096
  );
2767
- } else if (!response.ok) {
2768
- console.warn(
2769
- "\u26A0\uFE0F MindCache OAuth: API responded with status " + response.status + "\n URL: " + this.config.apiUrl
2770
- );
2771
- } else {
2772
- console.log("\u2705 MindCache OAuth: API is reachable at " + this.config.apiUrl);
2773
3097
  }
2774
3098
  } catch (error) {
2775
3099
  console.error(
2776
- "\u274C MindCache OAuth ERROR: Cannot reach API at " + this.config.apiUrl + "\n Error: " + (error instanceof Error ? error.message : String(error)) + "\n Check your network connection and baseUrl configuration."
3100
+ "\u274C MindCache OAuth ERROR: Cannot reach API at " + this.config.baseUrl + "\n Error: " + (error instanceof Error ? error.message : String(error))
2777
3101
  );
2778
3102
  }
2779
3103
  }
@@ -2802,7 +3126,7 @@ var OAuthClient = class {
2802
3126
  async authorize(options) {
2803
3127
  const state = options?.state || generateRandomString(32);
2804
3128
  this.setStorage("state", state);
2805
- const url = new URL(this.config.authUrl);
3129
+ const url = new URL(this.authUrl);
2806
3130
  url.searchParams.set("response_type", "code");
2807
3131
  url.searchParams.set("client_id", this.config.clientId);
2808
3132
  url.searchParams.set("redirect_uri", this.config.redirectUri);
@@ -2865,7 +3189,7 @@ var OAuthClient = class {
2865
3189
  }
2866
3190
  body.code_verifier = codeVerifier;
2867
3191
  }
2868
- const response = await fetch(this.config.tokenUrl, {
3192
+ const response = await fetch(this.tokenUrl, {
2869
3193
  method: "POST",
2870
3194
  headers: {
2871
3195
  "Content-Type": "application/json"
@@ -2917,7 +3241,7 @@ var OAuthClient = class {
2917
3241
  throw new Error("No refresh token available");
2918
3242
  }
2919
3243
  try {
2920
- const response = await fetch(this.config.tokenUrl, {
3244
+ const response = await fetch(this.tokenUrl, {
2921
3245
  method: "POST",
2922
3246
  headers: {
2923
3247
  "Content-Type": "application/json"
@@ -2951,7 +3275,7 @@ var OAuthClient = class {
2951
3275
  */
2952
3276
  async getUserInfo() {
2953
3277
  const token = await this.getAccessToken();
2954
- const response = await fetch(DEFAULT_USERINFO_URL, {
3278
+ const response = await fetch(this.userinfoUrl, {
2955
3279
  headers: {
2956
3280
  Authorization: `Bearer ${token}`
2957
3281
  }
@@ -2973,7 +3297,7 @@ var OAuthClient = class {
2973
3297
  async logout() {
2974
3298
  if (this.tokens?.accessToken) {
2975
3299
  try {
2976
- await fetch(this.config.tokenUrl.replace("/token", "/revoke"), {
3300
+ await fetch(this.tokenUrl.replace("/token", "/revoke"), {
2977
3301
  method: "POST",
2978
3302
  headers: {
2979
3303
  "Content-Type": "application/json"
@@ -2996,7 +3320,7 @@ var OAuthClient = class {
2996
3320
  }
2997
3321
  /**
2998
3322
  * Token provider for MindCache cloud config
2999
- * This fetches a WebSocket token (short-lived) using the OAuth access token
3323
+ * This fetches a WebSocket token using the OAuth access token
3000
3324
  * Use this with MindCacheCloudOptions.tokenProvider
3001
3325
  */
3002
3326
  tokenProvider = async () => {
@@ -3005,7 +3329,7 @@ var OAuthClient = class {
3005
3329
  if (!instanceId) {
3006
3330
  throw new Error("No instance ID available. Complete OAuth flow first.");
3007
3331
  }
3008
- const response = await fetch(`${this.config.apiUrl}/api/ws-token`, {
3332
+ const response = await fetch(`${this.config.baseUrl}/api/ws-token`, {
3009
3333
  method: "POST",
3010
3334
  headers: {
3011
3335
  "Content-Type": "application/json",
@@ -3025,7 +3349,6 @@ var OAuthClient = class {
3025
3349
  };
3026
3350
  /**
3027
3351
  * Get raw OAuth access token (for API calls, not WebSocket)
3028
- * Use getAccessToken() for most cases
3029
3352
  */
3030
3353
  accessTokenProvider = async () => {
3031
3354
  return this.getAccessToken();
@@ -3117,6 +3440,7 @@ var mindcache = new MindCache();
3117
3440
  exports.DEFAULT_KEY_ATTRIBUTES = DEFAULT_KEY_ATTRIBUTES;
3118
3441
  exports.MindCache = MindCache;
3119
3442
  exports.OAuthClient = OAuthClient;
3443
+ exports.SchemaParser = SchemaParser;
3120
3444
  exports.SystemTagHelpers = SystemTagHelpers;
3121
3445
  exports.createOAuthClient = createOAuthClient;
3122
3446
  exports.mindcache = mindcache;