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.
package/dist/index.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
  import { useState, useRef, useEffect } from 'react';
8
10
 
9
11
  var __defProp = Object.defineProperty;
@@ -184,7 +186,6 @@ var init_CloudAdapter = __esm({
184
186
  if (!config.baseUrl) {
185
187
  throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
186
188
  }
187
- this.validateConfig(config);
188
189
  this.setupNetworkDetection();
189
190
  }
190
191
  ws = null;
@@ -200,43 +201,6 @@ var init_CloudAdapter = __esm({
200
201
  handleOnline = null;
201
202
  handleOffline = null;
202
203
  _synced = false;
203
- /**
204
- * Validate configuration and warn about common mistakes
205
- */
206
- validateConfig(config) {
207
- const baseUrl = config.baseUrl;
208
- if (!baseUrl) {
209
- return;
210
- }
211
- console.log("\u2601\uFE0F MindCache Cloud Config:", {
212
- baseUrl,
213
- instanceId: config.instanceId,
214
- hasTokenProvider: !!config.tokenProvider,
215
- hasApiKey: !!config.apiKey
216
- });
217
- try {
218
- const url = new URL(baseUrl);
219
- if (url.hostname === "mindcache.dev") {
220
- console.error(
221
- '\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)."
222
- );
223
- }
224
- if (url.protocol === "ws:" || url.protocol === "wss:") {
225
- console.warn(
226
- "\u26A0\uFE0F MindCache Cloud: baseUrl uses WebSocket protocol (" + url.protocol + "). Consider using http:// or https:// - CloudAdapter will handle the WebSocket upgrade automatically."
227
- );
228
- }
229
- if (url.hostname === "localhost" && url.port !== "8787" && url.port !== "3000") {
230
- console.warn(
231
- "\u26A0\uFE0F MindCache Cloud: localhost URL detected with non-standard port " + url.port + ". Default MindCache dev server runs on port 8787."
232
- );
233
- }
234
- const wsUrl = baseUrl.replace("https://", "wss://").replace("http://", "ws://");
235
- console.log("\u2601\uFE0F WebSocket will connect to:", wsUrl + "/sync/" + config.instanceId);
236
- } catch (e) {
237
- console.error("\u26A0\uFE0F MindCache Cloud: Invalid baseUrl format:", baseUrl);
238
- }
239
- }
240
204
  /** Browser network status - instantly updated via navigator.onLine */
241
205
  get isOnline() {
242
206
  return this._isOnline;
@@ -489,6 +453,76 @@ var SystemTagHelpers = {
489
453
  hasTemplateInjection: (attrs) => attrs.systemTags.includes("ApplyTemplate")
490
454
  };
491
455
 
456
+ // src/core/SchemaParser.ts
457
+ var SchemaParser = class {
458
+ /**
459
+ * Parse a markdown schema string into a CustomTypeDefinition
460
+ * @param schema - Markdown schema string
461
+ * @returns Parsed type definition
462
+ * @throws Error if schema format is invalid
463
+ */
464
+ static parse(schema) {
465
+ const lines = schema.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
466
+ if (lines.length === 0) {
467
+ throw new Error("Schema cannot be empty");
468
+ }
469
+ const typeNameMatch = lines[0].match(/^#\s*(\w+)$/);
470
+ if (!typeNameMatch) {
471
+ throw new Error(`Invalid schema: first line must be "#TypeName", got "${lines[0]}"`);
472
+ }
473
+ const typeName = typeNameMatch[1];
474
+ const fields = [];
475
+ for (let i = 1; i < lines.length; i++) {
476
+ const line = lines[i];
477
+ const fieldMatch = line.match(/^\*\s*([^:]+):\s*(.+)$/);
478
+ if (fieldMatch) {
479
+ fields.push({
480
+ name: fieldMatch[1].trim(),
481
+ description: fieldMatch[2].trim()
482
+ });
483
+ }
484
+ }
485
+ if (fields.length === 0) {
486
+ throw new Error(`Schema "${typeName}" must have at least one field`);
487
+ }
488
+ return {
489
+ name: typeName,
490
+ fields,
491
+ rawSchema: schema
492
+ };
493
+ }
494
+ /**
495
+ * Generate a markdown representation of a type definition
496
+ * Useful for including in LLM prompts
497
+ */
498
+ static toMarkdown(typeDef) {
499
+ const lines = [`#${typeDef.name}`];
500
+ for (const field of typeDef.fields) {
501
+ lines.push(`* ${field.name}: ${field.description}`);
502
+ }
503
+ return lines.join("\n");
504
+ }
505
+ /**
506
+ * Generate a prompt-friendly description of the type
507
+ * More verbose than toMarkdown, better for LLM guidance
508
+ */
509
+ static toPromptDescription(typeDef) {
510
+ const fieldDescs = typeDef.fields.map((f) => ` - ${f.name}: ${f.description}`).join("\n");
511
+ return `Type "${typeDef.name}" with fields:
512
+ ${fieldDescs}`;
513
+ }
514
+ /**
515
+ * Generate an example value structure based on the type definition
516
+ */
517
+ static generateExample(typeDef) {
518
+ const lines = [`#${typeDef.name.toLowerCase()}`];
519
+ for (const field of typeDef.fields) {
520
+ lines.push(`* ${field.name}: [${field.description}]`);
521
+ }
522
+ return lines.join("\n");
523
+ }
524
+ };
525
+
492
526
  // src/core/MarkdownSerializer.ts
493
527
  var MarkdownSerializer = class {
494
528
  /**
@@ -835,18 +869,16 @@ var MarkdownSerializer = class {
835
869
  return result;
836
870
  }
837
871
  };
838
-
839
- // src/core/AIToolBuilder.ts
840
872
  var AIToolBuilder = class _AIToolBuilder {
841
873
  /**
842
- * Sanitize key name for use in tool names
843
- */
874
+ * Sanitize key name for use in tool names
875
+ */
844
876
  static sanitizeKeyForTool(key) {
845
877
  return key.replace(/[^a-zA-Z0-9_-]/g, "_");
846
878
  }
847
879
  /**
848
- * Find original key from sanitized tool name
849
- */
880
+ * Find original key from sanitized tool name
881
+ */
850
882
  static findKeyFromSanitizedTool(mc, sanitizedKey) {
851
883
  for (const key of mc.keys()) {
852
884
  if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
@@ -856,15 +888,174 @@ var AIToolBuilder = class _AIToolBuilder {
856
888
  return void 0;
857
889
  }
858
890
  /**
859
- * Generate Vercel AI SDK compatible tools for writable keys.
860
- * For document type keys, generates additional tools: append_, insert_, edit_
861
- *
862
- * Security: All tools use llm_set_key internally which:
863
- * - Only modifies VALUES, never attributes/systemTags
864
- * - Prevents LLMs from escalating privileges
865
- */
891
+ * Generate framework-agnostic tools with raw JSON Schema.
892
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, etc.
893
+ *
894
+ * Tool format:
895
+ * {
896
+ * description: string,
897
+ * parameters: { type: 'object', properties: {...}, required: [...] },
898
+ * execute: async (args) => result
899
+ * }
900
+ */
901
+ static createTools(mc) {
902
+ return _AIToolBuilder._buildTools(mc);
903
+ }
904
+ /**
905
+ * Generate Vercel AI SDK v5 compatible tools using Zod schemas.
906
+ * Uses tool() helper with Zod for full AI SDK v5 compatibility.
907
+ *
908
+ * Use this with: generateText(), streamText() from 'ai' package
909
+ */
866
910
  static createVercelAITools(mc) {
867
911
  const tools = {};
912
+ const registeredTypes = mc.getRegisteredTypes();
913
+ const typeDesc = registeredTypes.length > 0 ? `Optional type: ${registeredTypes.join(" | ")}` : "No types registered";
914
+ tools["create_key"] = tool({
915
+ description: `Create a new key in MindCache. ${registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : ""}`,
916
+ inputSchema: z.object({
917
+ key: z.string().describe('The key name (e.g., "contact_john_doe")'),
918
+ value: z.string().describe("The value (JSON string for structured data)"),
919
+ type: z.string().optional().describe(typeDesc)
920
+ }),
921
+ execute: async ({ key, value, type }) => {
922
+ if (mc.has(key)) {
923
+ return { result: `Key "${key}" exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)}`, error: true };
924
+ }
925
+ if (type && !mc.getTypeSchema(type)) {
926
+ return { result: `Type "${type}" not registered`, error: true };
927
+ }
928
+ mc.set_value(key, value, { systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"] });
929
+ if (type) {
930
+ mc.setType(key, type);
931
+ }
932
+ return { result: `Created "${key}"${type ? ` (${type})` : ""}`, key, value };
933
+ }
934
+ });
935
+ for (const key of mc.keys()) {
936
+ if (key.startsWith("$") || !mc.keyMatchesContext(key)) {
937
+ continue;
938
+ }
939
+ const attrs = mc.get_attributes(key);
940
+ if (!attrs?.systemTags?.includes("LLMWrite")) {
941
+ continue;
942
+ }
943
+ const sanitized = _AIToolBuilder.sanitizeKeyForTool(key);
944
+ const customTypeName = mc.getKeyType(key);
945
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
946
+ let desc = `Write to "${key}"`;
947
+ if (customType) {
948
+ desc = `Write to "${key}" (${customTypeName}). ${SchemaParser.toPromptDescription(customType)}`;
949
+ }
950
+ tools[`write_${sanitized}`] = tool({
951
+ description: desc,
952
+ inputSchema: z.object({
953
+ value: z.string().describe(customType ? `JSON following ${customTypeName} schema` : "Value to write")
954
+ }),
955
+ execute: async ({ value }) => {
956
+ const success = mc.llm_set_key(key, value);
957
+ return success ? { result: `Wrote to ${key}`, key, value } : { result: `Failed to write to ${key}`, error: true };
958
+ }
959
+ });
960
+ if (attrs?.type === "document") {
961
+ tools[`append_${sanitized}`] = tool({
962
+ description: `Append to "${key}" document`,
963
+ inputSchema: z.object({ text: z.string().describe("Text to append") }),
964
+ execute: async ({ text }) => {
965
+ const yText = mc.get_document(key);
966
+ if (yText) {
967
+ yText.insert(yText.length, text);
968
+ return { result: "Appended", key };
969
+ }
970
+ return { result: "Not found", error: true };
971
+ }
972
+ });
973
+ tools[`insert_${sanitized}`] = tool({
974
+ description: `Insert text at position in "${key}" document`,
975
+ inputSchema: z.object({
976
+ index: z.number().describe("Position (0 = start)"),
977
+ text: z.string().describe("Text to insert")
978
+ }),
979
+ execute: async ({ index, text }) => {
980
+ mc.insert_text(key, index, text);
981
+ return { result: `Inserted at ${index}`, key };
982
+ }
983
+ });
984
+ tools[`edit_${sanitized}`] = tool({
985
+ description: `Find and replace in "${key}" document`,
986
+ inputSchema: z.object({
987
+ find: z.string().describe("Text to find"),
988
+ replace: z.string().describe("Replacement")
989
+ }),
990
+ execute: async ({ find, replace }) => {
991
+ const yText = mc.get_document(key);
992
+ if (yText) {
993
+ const text = yText.toString();
994
+ const idx = text.indexOf(find);
995
+ if (idx !== -1) {
996
+ yText.delete(idx, find.length);
997
+ yText.insert(idx, replace);
998
+ return { result: `Replaced "${find}"`, key };
999
+ }
1000
+ return { result: `"${find}" not found`, error: true };
1001
+ }
1002
+ return { result: "Document not found", error: true };
1003
+ }
1004
+ });
1005
+ }
1006
+ }
1007
+ return tools;
1008
+ }
1009
+ /**
1010
+ * Internal: Build tools with raw JSON Schema (framework-agnostic).
1011
+ */
1012
+ static _buildTools(mc) {
1013
+ const tools = {};
1014
+ const registeredTypes = mc.getRegisteredTypes();
1015
+ const typeInfo = registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : "No custom types registered";
1016
+ tools["create_key"] = {
1017
+ description: `Create a new key in MindCache. ${typeInfo}. The new key will be readable and writable by the LLM.`,
1018
+ parameters: {
1019
+ type: "object",
1020
+ properties: {
1021
+ key: { type: "string", description: 'The key name to create (e.g., "contact_john_doe")' },
1022
+ value: { type: "string", description: "The value to store (use JSON string for structured data)" },
1023
+ type: {
1024
+ type: "string",
1025
+ description: registeredTypes.length > 0 ? `Optional: custom type name (${registeredTypes.join(" | ")})` : "Optional: custom type name (none registered)"
1026
+ }
1027
+ },
1028
+ required: ["key", "value"]
1029
+ },
1030
+ execute: async ({ key, value, type }) => {
1031
+ if (mc.has(key)) {
1032
+ return {
1033
+ result: `Key "${key}" already exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)} to update it.`,
1034
+ key,
1035
+ error: true
1036
+ };
1037
+ }
1038
+ if (type && !mc.getTypeSchema(type)) {
1039
+ return {
1040
+ result: `Type "${type}" is not registered. Available types: ${registeredTypes.join(", ") || "none"}`,
1041
+ key,
1042
+ error: true
1043
+ };
1044
+ }
1045
+ mc.set_value(key, value, {
1046
+ systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"]
1047
+ });
1048
+ if (type) {
1049
+ mc.setType(key, type);
1050
+ }
1051
+ return {
1052
+ result: `Successfully created key "${key}"${type ? ` with type "${type}"` : ""}`,
1053
+ key,
1054
+ value,
1055
+ type
1056
+ };
1057
+ }
1058
+ };
868
1059
  for (const key of mc.keys()) {
869
1060
  if (key.startsWith("$")) {
870
1061
  continue;
@@ -879,12 +1070,28 @@ var AIToolBuilder = class _AIToolBuilder {
879
1070
  }
880
1071
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
881
1072
  const isDocument = attributes?.type === "document";
1073
+ const customTypeName = mc.getKeyType(key);
1074
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1075
+ let writeDescription;
1076
+ if (customType) {
1077
+ const schemaGuidance = SchemaParser.toPromptDescription(customType);
1078
+ const example = SchemaParser.generateExample(customType);
1079
+ writeDescription = `Write a value to "${key}" that must follow this schema:
1080
+ ${schemaGuidance}
1081
+
1082
+ Example format:
1083
+ ${example}`;
1084
+ } else if (isDocument) {
1085
+ writeDescription = `Rewrite the entire "${key}" document`;
1086
+ } else {
1087
+ writeDescription = `Write a value to the STM key: ${key}`;
1088
+ }
882
1089
  tools[`write_${sanitizedKey}`] = {
883
- description: isDocument ? `Rewrite the entire "${key}" document` : `Write a value to the STM key: ${key}`,
884
- inputSchema: {
1090
+ description: writeDescription,
1091
+ parameters: {
885
1092
  type: "object",
886
1093
  properties: {
887
- value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
1094
+ value: { type: "string", description: customType ? `Value following ${customTypeName} schema` : isDocument ? "New document content" : "The value to write" }
888
1095
  },
889
1096
  required: ["value"]
890
1097
  },
@@ -907,7 +1114,7 @@ var AIToolBuilder = class _AIToolBuilder {
907
1114
  if (isDocument) {
908
1115
  tools[`append_${sanitizedKey}`] = {
909
1116
  description: `Append text to the end of "${key}" document`,
910
- inputSchema: {
1117
+ parameters: {
911
1118
  type: "object",
912
1119
  properties: {
913
1120
  text: { type: "string", description: "Text to append" }
@@ -932,7 +1139,7 @@ var AIToolBuilder = class _AIToolBuilder {
932
1139
  };
933
1140
  tools[`insert_${sanitizedKey}`] = {
934
1141
  description: `Insert text at a position in "${key}" document`,
935
- inputSchema: {
1142
+ parameters: {
936
1143
  type: "object",
937
1144
  properties: {
938
1145
  index: { type: "number", description: "Position to insert at (0 = start)" },
@@ -955,7 +1162,7 @@ var AIToolBuilder = class _AIToolBuilder {
955
1162
  };
956
1163
  tools[`edit_${sanitizedKey}`] = {
957
1164
  description: `Find and replace text in "${key}" document`,
958
- inputSchema: {
1165
+ parameters: {
959
1166
  type: "object",
960
1167
  properties: {
961
1168
  find: { type: "string", description: "Text to find" },
@@ -992,11 +1199,16 @@ var AIToolBuilder = class _AIToolBuilder {
992
1199
  return tools;
993
1200
  }
994
1201
  /**
995
- * Generate a system prompt containing all visible STM keys and their values.
996
- * Indicates which tools can be used to modify writable keys.
997
- */
1202
+ * Generate a system prompt containing all visible STM keys and their values.
1203
+ * Indicates which tools can be used to modify writable keys.
1204
+ */
998
1205
  static getSystemPrompt(mc) {
999
1206
  const lines = [];
1207
+ const registeredTypes = mc.getRegisteredTypes();
1208
+ if (registeredTypes.length > 0) {
1209
+ lines.push(`[create_key tool available - registered types: ${registeredTypes.join(", ")}]`);
1210
+ lines.push("");
1211
+ }
1000
1212
  for (const key of mc.keys()) {
1001
1213
  if (key.startsWith("$")) {
1002
1214
  continue;
@@ -1014,8 +1226,18 @@ var AIToolBuilder = class _AIToolBuilder {
1014
1226
  const isWritable = attributes?.systemTags?.includes("LLMWrite");
1015
1227
  const isDocument = attributes?.type === "document";
1016
1228
  const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
1229
+ const customTypeName = mc.getKeyType(key);
1230
+ const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
1017
1231
  if (isWritable) {
1018
- if (isDocument) {
1232
+ if (customType) {
1233
+ const schemaInfo = SchemaParser.toMarkdown(customType);
1234
+ lines.push(
1235
+ `${key} (type: ${customTypeName}): ${displayValue}
1236
+ Schema:
1237
+ ${schemaInfo}
1238
+ Tool: write_${sanitizedKey}`
1239
+ );
1240
+ } else if (isDocument) {
1019
1241
  lines.push(
1020
1242
  `${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
1021
1243
  );
@@ -1026,15 +1248,19 @@ var AIToolBuilder = class _AIToolBuilder {
1026
1248
  );
1027
1249
  }
1028
1250
  } else {
1029
- lines.push(`${key}: ${displayValue}`);
1251
+ if (customTypeName) {
1252
+ lines.push(`${key} (type: ${customTypeName}): ${displayValue}`);
1253
+ } else {
1254
+ lines.push(`${key}: ${displayValue}`);
1255
+ }
1030
1256
  }
1031
1257
  }
1032
1258
  return lines.join("\n");
1033
1259
  }
1034
1260
  /**
1035
- * Execute a tool call by name with the given value.
1036
- * Returns the result or null if tool not found.
1037
- */
1261
+ * Execute a tool call by name with the given value.
1262
+ * Returns the result or null if tool not found.
1263
+ */
1038
1264
  static executeToolCall(mc, toolName, value) {
1039
1265
  const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
1040
1266
  if (!match) {
@@ -1361,7 +1587,7 @@ var MindCache = class {
1361
1587
  listeners = {};
1362
1588
  globalListeners = [];
1363
1589
  // Metadata
1364
- version = "3.3.2";
1590
+ version = "3.6.0";
1365
1591
  // Internal flag to prevent sync loops when receiving remote updates
1366
1592
  // (Less critical with Yjs but kept for API compat)
1367
1593
  normalizeSystemTags(tags) {
@@ -1398,6 +1624,8 @@ var MindCache = class {
1398
1624
  _history = [];
1399
1625
  _historyOptions = { maxEntries: 100, snapshotInterval: 10 };
1400
1626
  _historyEnabled = false;
1627
+ // Custom type registry
1628
+ _typeRegistry = /* @__PURE__ */ new Map();
1401
1629
  constructor(options) {
1402
1630
  this.doc = options?.doc || new Y.Doc();
1403
1631
  this.rootMap = this.doc.getMap("mindcache");
@@ -2354,6 +2582,71 @@ var MindCache = class {
2354
2582
  systemGetKeysByTag(tag) {
2355
2583
  return TagManager.systemGetKeysByTag(this, tag);
2356
2584
  }
2585
+ // ============================================
2586
+ // Custom Type Methods
2587
+ // ============================================
2588
+ /**
2589
+ * Register a custom type with a markdown schema definition.
2590
+ *
2591
+ * Schema format:
2592
+ * ```
2593
+ * #TypeName
2594
+ * * fieldName: description of the field
2595
+ * * anotherField: description
2596
+ * ```
2597
+ *
2598
+ * @param name - Type name (e.g., 'Contact')
2599
+ * @param schema - Markdown schema definition
2600
+ * @throws Error if schema format is invalid
2601
+ */
2602
+ registerType(name, schema) {
2603
+ const typeDef = SchemaParser.parse(schema);
2604
+ typeDef.name = name;
2605
+ this._typeRegistry.set(name, typeDef);
2606
+ }
2607
+ /**
2608
+ * Assign a custom type to a key.
2609
+ * The key must exist and the type must be registered.
2610
+ * Also sets the underlying type to 'json' since custom types are structured JSON data.
2611
+ *
2612
+ * @param key - Key to assign type to
2613
+ * @param typeName - Registered type name
2614
+ * @throws Error if key doesn't exist or type is not registered
2615
+ */
2616
+ setType(key, typeName) {
2617
+ if (!this.rootMap.has(key)) {
2618
+ throw new Error(`Key "${key}" does not exist`);
2619
+ }
2620
+ if (!this._typeRegistry.has(typeName)) {
2621
+ throw new Error(`Type "${typeName}" is not registered. Use registerType() first.`);
2622
+ }
2623
+ this.set_attributes(key, { type: "json", customType: typeName });
2624
+ }
2625
+ /**
2626
+ * Get a registered type schema definition.
2627
+ *
2628
+ * @param typeName - Type name to look up
2629
+ * @returns The type definition or undefined if not registered
2630
+ */
2631
+ getTypeSchema(typeName) {
2632
+ return this._typeRegistry.get(typeName);
2633
+ }
2634
+ /**
2635
+ * Get all registered type names.
2636
+ */
2637
+ getRegisteredTypes() {
2638
+ return Array.from(this._typeRegistry.keys());
2639
+ }
2640
+ /**
2641
+ * Get the custom type assigned to a key.
2642
+ *
2643
+ * @param key - Key to check
2644
+ * @returns Type name or undefined if no custom type assigned
2645
+ */
2646
+ getKeyType(key) {
2647
+ const attrs = this.get_attributes(key);
2648
+ return attrs?.customType;
2649
+ }
2357
2650
  /**
2358
2651
  * Helper to get sorted keys (by zIndex).
2359
2652
  * Respects context filtering when set.
@@ -2615,9 +2908,28 @@ var MindCache = class {
2615
2908
  findKeyFromSanitizedTool(sanitizedKey) {
2616
2909
  return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
2617
2910
  }
2911
+ /**
2912
+ * Generate framework-agnostic tools with raw JSON Schema.
2913
+ * Works with: OpenAI SDK, Anthropic SDK, LangChain, and other frameworks.
2914
+ *
2915
+ * Tool format:
2916
+ * {
2917
+ * description: string,
2918
+ * parameters: { type: 'object', properties: {...}, required: [...] },
2919
+ * execute: async (args) => result
2920
+ * }
2921
+ *
2922
+ * Security: All tools use llm_set_key internally which:
2923
+ * - Only modifies VALUES, never attributes/systemTags
2924
+ * - Prevents LLMs from escalating privileges
2925
+ */
2926
+ create_tools() {
2927
+ return AIToolBuilder.createTools(this);
2928
+ }
2618
2929
  /**
2619
2930
  * Generate Vercel AI SDK compatible tools for writable keys.
2620
- * For document type keys, generates additional tools: append_, insert_, edit_
2931
+ * Wraps parameters with jsonSchema() for AI SDK v5 compatibility.
2932
+ * Use this with: generateText(), streamText() from 'ai' package.
2621
2933
  *
2622
2934
  * Security: All tools use llm_set_key internally which:
2623
2935
  * - Only modifies VALUES, never attributes/systemTags
@@ -2660,9 +2972,6 @@ init_CloudAdapter();
2660
2972
  init_CloudAdapter();
2661
2973
 
2662
2974
  // src/cloud/OAuthClient.ts
2663
- var DEFAULT_AUTH_URL = "https://api.mindcache.dev/oauth/authorize";
2664
- var DEFAULT_TOKEN_URL = "https://api.mindcache.dev/oauth/token";
2665
- var DEFAULT_USERINFO_URL = "https://api.mindcache.dev/oauth/userinfo";
2666
2975
  var TOKEN_REFRESH_BUFFER = 5 * 60 * 1e3;
2667
2976
  function generateRandomString(length2) {
2668
2977
  const array = new Uint8Array(length2);
@@ -2691,6 +3000,21 @@ var OAuthClient = class {
2691
3000
  tokens = null;
2692
3001
  refreshPromise = null;
2693
3002
  constructor(config) {
3003
+ if (!config.baseUrl) {
3004
+ throw new Error(
3005
+ 'MindCache OAuth: baseUrl is required!\n For production: baseUrl: "https://api.mindcache.dev"\n For local dev: baseUrl: "http://localhost:8787"'
3006
+ );
3007
+ }
3008
+ try {
3009
+ const url = new URL(config.baseUrl);
3010
+ if (url.hostname === "mindcache.dev") {
3011
+ console.error(
3012
+ '\u274C MindCache OAuth ERROR: baseUrl should be "api.mindcache.dev" not "mindcache.dev"\n Current: ' + config.baseUrl + "\n Correct: https://api.mindcache.dev"
3013
+ );
3014
+ }
3015
+ } catch {
3016
+ throw new Error("MindCache OAuth: Invalid baseUrl format: " + config.baseUrl);
3017
+ }
2694
3018
  let redirectUri = config.redirectUri;
2695
3019
  if (!redirectUri && typeof window !== "undefined") {
2696
3020
  const url = new URL(window.location.href);
@@ -2698,19 +3022,57 @@ var OAuthClient = class {
2698
3022
  url.hash = "";
2699
3023
  redirectUri = url.toString();
2700
3024
  }
3025
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
2701
3026
  this.config = {
2702
3027
  clientId: config.clientId,
3028
+ baseUrl,
2703
3029
  redirectUri: redirectUri || "",
2704
3030
  scopes: config.scopes || ["read", "write"],
2705
- authUrl: config.authUrl || DEFAULT_AUTH_URL,
2706
- tokenUrl: config.tokenUrl || DEFAULT_TOKEN_URL,
2707
- apiUrl: config.apiUrl || (config.tokenUrl || DEFAULT_TOKEN_URL).replace("/oauth/token", ""),
2708
3031
  usePKCE: config.usePKCE !== false,
2709
3032
  // Default true
2710
3033
  storagePrefix: config.storagePrefix || "mindcache_oauth"
2711
3034
  };
3035
+ console.log("\u{1F510} MindCache OAuth:", {
3036
+ baseUrl: this.config.baseUrl,
3037
+ authUrl: this.authUrl,
3038
+ tokenUrl: this.tokenUrl,
3039
+ clientId: this.config.clientId.substring(0, 20) + "..."
3040
+ });
3041
+ this.validateApi();
2712
3042
  this.loadTokens();
2713
3043
  }
3044
+ /** Derived auth URL */
3045
+ get authUrl() {
3046
+ return this.config.baseUrl + "/oauth/authorize";
3047
+ }
3048
+ /** Derived token URL */
3049
+ get tokenUrl() {
3050
+ return this.config.baseUrl + "/oauth/token";
3051
+ }
3052
+ /** Derived userinfo URL */
3053
+ get userinfoUrl() {
3054
+ return this.config.baseUrl + "/oauth/userinfo";
3055
+ }
3056
+ /**
3057
+ * Validate the API is reachable
3058
+ */
3059
+ async validateApi() {
3060
+ try {
3061
+ const response = await fetch(`${this.config.baseUrl}/oauth/apps/info`, {
3062
+ method: "GET",
3063
+ headers: { "Accept": "application/json" }
3064
+ });
3065
+ if (response.status === 404) {
3066
+ console.error(
3067
+ "\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'
3068
+ );
3069
+ }
3070
+ } catch (error) {
3071
+ console.error(
3072
+ "\u274C MindCache OAuth ERROR: Cannot reach API at " + this.config.baseUrl + "\n Error: " + (error instanceof Error ? error.message : String(error))
3073
+ );
3074
+ }
3075
+ }
2714
3076
  /**
2715
3077
  * Check if user is authenticated
2716
3078
  */
@@ -2736,7 +3098,7 @@ var OAuthClient = class {
2736
3098
  async authorize(options) {
2737
3099
  const state = options?.state || generateRandomString(32);
2738
3100
  this.setStorage("state", state);
2739
- const url = new URL(this.config.authUrl);
3101
+ const url = new URL(this.authUrl);
2740
3102
  url.searchParams.set("response_type", "code");
2741
3103
  url.searchParams.set("client_id", this.config.clientId);
2742
3104
  url.searchParams.set("redirect_uri", this.config.redirectUri);
@@ -2799,7 +3161,7 @@ var OAuthClient = class {
2799
3161
  }
2800
3162
  body.code_verifier = codeVerifier;
2801
3163
  }
2802
- const response = await fetch(this.config.tokenUrl, {
3164
+ const response = await fetch(this.tokenUrl, {
2803
3165
  method: "POST",
2804
3166
  headers: {
2805
3167
  "Content-Type": "application/json"
@@ -2851,7 +3213,7 @@ var OAuthClient = class {
2851
3213
  throw new Error("No refresh token available");
2852
3214
  }
2853
3215
  try {
2854
- const response = await fetch(this.config.tokenUrl, {
3216
+ const response = await fetch(this.tokenUrl, {
2855
3217
  method: "POST",
2856
3218
  headers: {
2857
3219
  "Content-Type": "application/json"
@@ -2885,7 +3247,7 @@ var OAuthClient = class {
2885
3247
  */
2886
3248
  async getUserInfo() {
2887
3249
  const token = await this.getAccessToken();
2888
- const response = await fetch(DEFAULT_USERINFO_URL, {
3250
+ const response = await fetch(this.userinfoUrl, {
2889
3251
  headers: {
2890
3252
  Authorization: `Bearer ${token}`
2891
3253
  }
@@ -2907,7 +3269,7 @@ var OAuthClient = class {
2907
3269
  async logout() {
2908
3270
  if (this.tokens?.accessToken) {
2909
3271
  try {
2910
- await fetch(this.config.tokenUrl.replace("/token", "/revoke"), {
3272
+ await fetch(this.tokenUrl.replace("/token", "/revoke"), {
2911
3273
  method: "POST",
2912
3274
  headers: {
2913
3275
  "Content-Type": "application/json"
@@ -2930,7 +3292,7 @@ var OAuthClient = class {
2930
3292
  }
2931
3293
  /**
2932
3294
  * Token provider for MindCache cloud config
2933
- * This fetches a WebSocket token (short-lived) using the OAuth access token
3295
+ * This fetches a WebSocket token using the OAuth access token
2934
3296
  * Use this with MindCacheCloudOptions.tokenProvider
2935
3297
  */
2936
3298
  tokenProvider = async () => {
@@ -2939,7 +3301,7 @@ var OAuthClient = class {
2939
3301
  if (!instanceId) {
2940
3302
  throw new Error("No instance ID available. Complete OAuth flow first.");
2941
3303
  }
2942
- const response = await fetch(`${this.config.apiUrl}/api/ws-token`, {
3304
+ const response = await fetch(`${this.config.baseUrl}/api/ws-token`, {
2943
3305
  method: "POST",
2944
3306
  headers: {
2945
3307
  "Content-Type": "application/json",
@@ -2959,7 +3321,6 @@ var OAuthClient = class {
2959
3321
  };
2960
3322
  /**
2961
3323
  * Get raw OAuth access token (for API calls, not WebSocket)
2962
- * Use getAccessToken() for most cases
2963
3324
  */
2964
3325
  accessTokenProvider = async () => {
2965
3326
  return this.getAccessToken();
@@ -3048,6 +3409,6 @@ function useMindCache(options) {
3048
3409
  // src/index.ts
3049
3410
  var mindcache = new MindCache();
3050
3411
 
3051
- export { CloudAdapter, DEFAULT_KEY_ATTRIBUTES, IndexedDBAdapter, MindCache, OAuthClient, SystemTagHelpers, createOAuthClient, mindcache, useMindCache };
3412
+ export { CloudAdapter, DEFAULT_KEY_ATTRIBUTES, IndexedDBAdapter, MindCache, OAuthClient, SchemaParser, SystemTagHelpers, createOAuthClient, mindcache, useMindCache };
3052
3413
  //# sourceMappingURL=index.mjs.map
3053
3414
  //# sourceMappingURL=index.mjs.map