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.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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
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:
|
|
884
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
996
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
1036
|
-
|
|
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.
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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.
|
|
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
|
|
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.
|
|
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
|