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/README.md +29 -4
- package/dist/{CloudAdapter-DK4YecbV.d.mts → CloudAdapter-PLGvGjoA.d.mts} +86 -7
- package/dist/{CloudAdapter-DK4YecbV.d.ts → CloudAdapter-PLGvGjoA.d.ts} +86 -7
- package/dist/cloud/index.d.mts +2 -2
- package/dist/cloud/index.d.ts +2 -2
- package/dist/cloud/index.js +379 -67
- package/dist/cloud/index.js.map +1 -1
- package/dist/cloud/index.mjs +379 -67
- package/dist/cloud/index.mjs.map +1 -1
- package/dist/index.d.mts +55 -11
- package/dist/index.d.ts +55 -11
- package/dist/index.js +443 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +443 -82
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +379 -67
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +379 -67
- package/dist/server.mjs.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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:
|
|
912
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1024
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
1064
|
-
|
|
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.
|
|
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
|
-
*
|
|
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,9 +3000,6 @@ init_CloudAdapter();
|
|
|
2688
3000
|
init_CloudAdapter();
|
|
2689
3001
|
|
|
2690
3002
|
// src/cloud/OAuthClient.ts
|
|
2691
|
-
var DEFAULT_AUTH_URL = "https://api.mindcache.dev/oauth/authorize";
|
|
2692
|
-
var DEFAULT_TOKEN_URL = "https://api.mindcache.dev/oauth/token";
|
|
2693
|
-
var DEFAULT_USERINFO_URL = "https://api.mindcache.dev/oauth/userinfo";
|
|
2694
3003
|
var TOKEN_REFRESH_BUFFER = 5 * 60 * 1e3;
|
|
2695
3004
|
function generateRandomString(length2) {
|
|
2696
3005
|
const array = new Uint8Array(length2);
|
|
@@ -2719,6 +3028,21 @@ var OAuthClient = class {
|
|
|
2719
3028
|
tokens = null;
|
|
2720
3029
|
refreshPromise = null;
|
|
2721
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
|
+
}
|
|
2722
3046
|
let redirectUri = config.redirectUri;
|
|
2723
3047
|
if (!redirectUri && typeof window !== "undefined") {
|
|
2724
3048
|
const url = new URL(window.location.href);
|
|
@@ -2726,19 +3050,57 @@ var OAuthClient = class {
|
|
|
2726
3050
|
url.hash = "";
|
|
2727
3051
|
redirectUri = url.toString();
|
|
2728
3052
|
}
|
|
3053
|
+
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
2729
3054
|
this.config = {
|
|
2730
3055
|
clientId: config.clientId,
|
|
3056
|
+
baseUrl,
|
|
2731
3057
|
redirectUri: redirectUri || "",
|
|
2732
3058
|
scopes: config.scopes || ["read", "write"],
|
|
2733
|
-
authUrl: config.authUrl || DEFAULT_AUTH_URL,
|
|
2734
|
-
tokenUrl: config.tokenUrl || DEFAULT_TOKEN_URL,
|
|
2735
|
-
apiUrl: config.apiUrl || (config.tokenUrl || DEFAULT_TOKEN_URL).replace("/oauth/token", ""),
|
|
2736
3059
|
usePKCE: config.usePKCE !== false,
|
|
2737
3060
|
// Default true
|
|
2738
3061
|
storagePrefix: config.storagePrefix || "mindcache_oauth"
|
|
2739
3062
|
};
|
|
3063
|
+
console.log("\u{1F510} MindCache OAuth:", {
|
|
3064
|
+
baseUrl: this.config.baseUrl,
|
|
3065
|
+
authUrl: this.authUrl,
|
|
3066
|
+
tokenUrl: this.tokenUrl,
|
|
3067
|
+
clientId: this.config.clientId.substring(0, 20) + "..."
|
|
3068
|
+
});
|
|
3069
|
+
this.validateApi();
|
|
2740
3070
|
this.loadTokens();
|
|
2741
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
|
+
}
|
|
3084
|
+
/**
|
|
3085
|
+
* Validate the API is reachable
|
|
3086
|
+
*/
|
|
3087
|
+
async validateApi() {
|
|
3088
|
+
try {
|
|
3089
|
+
const response = await fetch(`${this.config.baseUrl}/oauth/apps/info`, {
|
|
3090
|
+
method: "GET",
|
|
3091
|
+
headers: { "Accept": "application/json" }
|
|
3092
|
+
});
|
|
3093
|
+
if (response.status === 404) {
|
|
3094
|
+
console.error(
|
|
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'
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
} catch (error) {
|
|
3099
|
+
console.error(
|
|
3100
|
+
"\u274C MindCache OAuth ERROR: Cannot reach API at " + this.config.baseUrl + "\n Error: " + (error instanceof Error ? error.message : String(error))
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
2742
3104
|
/**
|
|
2743
3105
|
* Check if user is authenticated
|
|
2744
3106
|
*/
|
|
@@ -2764,7 +3126,7 @@ var OAuthClient = class {
|
|
|
2764
3126
|
async authorize(options) {
|
|
2765
3127
|
const state = options?.state || generateRandomString(32);
|
|
2766
3128
|
this.setStorage("state", state);
|
|
2767
|
-
const url = new URL(this.
|
|
3129
|
+
const url = new URL(this.authUrl);
|
|
2768
3130
|
url.searchParams.set("response_type", "code");
|
|
2769
3131
|
url.searchParams.set("client_id", this.config.clientId);
|
|
2770
3132
|
url.searchParams.set("redirect_uri", this.config.redirectUri);
|
|
@@ -2827,7 +3189,7 @@ var OAuthClient = class {
|
|
|
2827
3189
|
}
|
|
2828
3190
|
body.code_verifier = codeVerifier;
|
|
2829
3191
|
}
|
|
2830
|
-
const response = await fetch(this.
|
|
3192
|
+
const response = await fetch(this.tokenUrl, {
|
|
2831
3193
|
method: "POST",
|
|
2832
3194
|
headers: {
|
|
2833
3195
|
"Content-Type": "application/json"
|
|
@@ -2879,7 +3241,7 @@ var OAuthClient = class {
|
|
|
2879
3241
|
throw new Error("No refresh token available");
|
|
2880
3242
|
}
|
|
2881
3243
|
try {
|
|
2882
|
-
const response = await fetch(this.
|
|
3244
|
+
const response = await fetch(this.tokenUrl, {
|
|
2883
3245
|
method: "POST",
|
|
2884
3246
|
headers: {
|
|
2885
3247
|
"Content-Type": "application/json"
|
|
@@ -2913,7 +3275,7 @@ var OAuthClient = class {
|
|
|
2913
3275
|
*/
|
|
2914
3276
|
async getUserInfo() {
|
|
2915
3277
|
const token = await this.getAccessToken();
|
|
2916
|
-
const response = await fetch(
|
|
3278
|
+
const response = await fetch(this.userinfoUrl, {
|
|
2917
3279
|
headers: {
|
|
2918
3280
|
Authorization: `Bearer ${token}`
|
|
2919
3281
|
}
|
|
@@ -2935,7 +3297,7 @@ var OAuthClient = class {
|
|
|
2935
3297
|
async logout() {
|
|
2936
3298
|
if (this.tokens?.accessToken) {
|
|
2937
3299
|
try {
|
|
2938
|
-
await fetch(this.
|
|
3300
|
+
await fetch(this.tokenUrl.replace("/token", "/revoke"), {
|
|
2939
3301
|
method: "POST",
|
|
2940
3302
|
headers: {
|
|
2941
3303
|
"Content-Type": "application/json"
|
|
@@ -2958,7 +3320,7 @@ var OAuthClient = class {
|
|
|
2958
3320
|
}
|
|
2959
3321
|
/**
|
|
2960
3322
|
* Token provider for MindCache cloud config
|
|
2961
|
-
* This fetches a WebSocket token
|
|
3323
|
+
* This fetches a WebSocket token using the OAuth access token
|
|
2962
3324
|
* Use this with MindCacheCloudOptions.tokenProvider
|
|
2963
3325
|
*/
|
|
2964
3326
|
tokenProvider = async () => {
|
|
@@ -2967,7 +3329,7 @@ var OAuthClient = class {
|
|
|
2967
3329
|
if (!instanceId) {
|
|
2968
3330
|
throw new Error("No instance ID available. Complete OAuth flow first.");
|
|
2969
3331
|
}
|
|
2970
|
-
const response = await fetch(`${this.config.
|
|
3332
|
+
const response = await fetch(`${this.config.baseUrl}/api/ws-token`, {
|
|
2971
3333
|
method: "POST",
|
|
2972
3334
|
headers: {
|
|
2973
3335
|
"Content-Type": "application/json",
|
|
@@ -2987,7 +3349,6 @@ var OAuthClient = class {
|
|
|
2987
3349
|
};
|
|
2988
3350
|
/**
|
|
2989
3351
|
* Get raw OAuth access token (for API calls, not WebSocket)
|
|
2990
|
-
* Use getAccessToken() for most cases
|
|
2991
3352
|
*/
|
|
2992
3353
|
accessTokenProvider = async () => {
|
|
2993
3354
|
return this.getAccessToken();
|
|
@@ -3079,6 +3440,7 @@ var mindcache = new MindCache();
|
|
|
3079
3440
|
exports.DEFAULT_KEY_ATTRIBUTES = DEFAULT_KEY_ATTRIBUTES;
|
|
3080
3441
|
exports.MindCache = MindCache;
|
|
3081
3442
|
exports.OAuthClient = OAuthClient;
|
|
3443
|
+
exports.SchemaParser = SchemaParser;
|
|
3082
3444
|
exports.SystemTagHelpers = SystemTagHelpers;
|
|
3083
3445
|
exports.createOAuthClient = createOAuthClient;
|
|
3084
3446
|
exports.mindcache = mindcache;
|