@t0ken.ai/memoryx-openclaw-plugin 1.0.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.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +164 -0
- package/openclaw.plugin.json +16 -0
- package/package.json +27 -0
- package/src/index.ts +191 -0
- package/tsconfig.json +17 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryX Real-time Plugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* 1. Real-time message capture to MemoryX
|
|
6
|
+
* 2. Auto-recall memories before agent starts
|
|
7
|
+
* 3. Compatible with memoryx-realtime-plugin (avoids duplication)
|
|
8
|
+
*/
|
|
9
|
+
export declare function onMessage(message: string, context: Record<string, any>): Promise<{
|
|
10
|
+
context: Record<string, any>;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function onResponse(response: string, context: Record<string, any>): string;
|
|
13
|
+
export declare function register(api: any): void;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6GH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAmB3C;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC3B,MAAM,CAER;AAGD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,QA0ChC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryX Real-time Plugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* 1. Real-time message capture to MemoryX
|
|
6
|
+
* 2. Auto-recall memories before agent starts
|
|
7
|
+
* 3. Compatible with memoryx-realtime-plugin (avoids duplication)
|
|
8
|
+
*/
|
|
9
|
+
// MemoryX API configuration
|
|
10
|
+
const MEMORYX_API_BASE = "http://t0ken.ai/api";
|
|
11
|
+
// Check if memoryx-realtime-plugin is installed
|
|
12
|
+
function isPluginInstalled() {
|
|
13
|
+
try {
|
|
14
|
+
const { execSync } = require("child_process");
|
|
15
|
+
const result = execSync("openclaw plugins list", {
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
timeout: 5000,
|
|
18
|
+
});
|
|
19
|
+
return (result.includes("memoryx-realtime") && result.includes("loaded"));
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Store message to MemoryX
|
|
26
|
+
async function storeToMemoryX(content, category = "semantic", metadata = {}) {
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(`${MEMORYX_API_BASE}/memories`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
content,
|
|
35
|
+
category,
|
|
36
|
+
project_id: "default",
|
|
37
|
+
metadata: {
|
|
38
|
+
...metadata,
|
|
39
|
+
source: "openclaw-realtime-plugin",
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
return response.ok;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Search MemoryX
|
|
51
|
+
async function searchMemoryX(query, limit = 3) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(`${MEMORYX_API_BASE}/memories/search`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: {
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
query,
|
|
60
|
+
limit,
|
|
61
|
+
project_id: "default",
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok)
|
|
65
|
+
return [];
|
|
66
|
+
const data = (await response.json());
|
|
67
|
+
return data.data || [];
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Check if content should be captured
|
|
74
|
+
function shouldCapture(text) {
|
|
75
|
+
if (!text || text.length < 5 || text.length > 500)
|
|
76
|
+
return false;
|
|
77
|
+
const skipPatterns = [
|
|
78
|
+
/^[好的ok谢谢嗯啊哈哈你好hihello拜拜再见]{1,3}$/i,
|
|
79
|
+
/^[??!!。,,\s]{1,5}$/,
|
|
80
|
+
];
|
|
81
|
+
for (const pattern of skipPatterns) {
|
|
82
|
+
if (pattern.test(text.trim()))
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const triggers = [
|
|
86
|
+
/记住|记一下|别忘了|save|remember/i,
|
|
87
|
+
/我喜欢|我讨厌|我习惯|我偏好|prefer|like|hate/i,
|
|
88
|
+
/我是|我在|我来自|i am|i work/i,
|
|
89
|
+
/纠正|更正|应该是|correct|actually/i,
|
|
90
|
+
/计划|打算|目标|plan|goal|will/i,
|
|
91
|
+
];
|
|
92
|
+
return triggers.some((pattern) => pattern.test(text));
|
|
93
|
+
}
|
|
94
|
+
// Detect category
|
|
95
|
+
function detectCategory(text) {
|
|
96
|
+
const lower = text.toLowerCase();
|
|
97
|
+
if (/prefer|like|hate|习惯|偏好|喜欢|讨厌/.test(lower))
|
|
98
|
+
return "preference";
|
|
99
|
+
if (/correct|纠正|更正/.test(lower))
|
|
100
|
+
return "correction";
|
|
101
|
+
if (/plan|goal|计划|打算/.test(lower))
|
|
102
|
+
return "plan";
|
|
103
|
+
return "semantic";
|
|
104
|
+
}
|
|
105
|
+
// OpenClaw Hook handlers
|
|
106
|
+
export async function onMessage(message, context) {
|
|
107
|
+
// Skip if memoryx-realtime-plugin is installed (avoid duplication)
|
|
108
|
+
if (isPluginInstalled()) {
|
|
109
|
+
return { context };
|
|
110
|
+
}
|
|
111
|
+
if (!shouldCapture(message)) {
|
|
112
|
+
return { context };
|
|
113
|
+
}
|
|
114
|
+
const category = detectCategory(message);
|
|
115
|
+
// Async store (non-blocking)
|
|
116
|
+
storeToMemoryX(message, category, {
|
|
117
|
+
from: context?.from,
|
|
118
|
+
channel: context?.channelId,
|
|
119
|
+
}).catch(() => { });
|
|
120
|
+
return { context };
|
|
121
|
+
}
|
|
122
|
+
export function onResponse(response, context) {
|
|
123
|
+
return response;
|
|
124
|
+
}
|
|
125
|
+
// Lifecycle hooks for OpenClaw Extension
|
|
126
|
+
export function register(api) {
|
|
127
|
+
api.logger.info("[MemoryX Realtime] Plugin registering...");
|
|
128
|
+
// 1. Message capture
|
|
129
|
+
api.on("message_received", async (event, ctx) => {
|
|
130
|
+
if (isPluginInstalled())
|
|
131
|
+
return;
|
|
132
|
+
const { content, from, timestamp } = event;
|
|
133
|
+
if (!shouldCapture(content))
|
|
134
|
+
return;
|
|
135
|
+
const category = detectCategory(content);
|
|
136
|
+
await storeToMemoryX(content, category, {
|
|
137
|
+
from,
|
|
138
|
+
channel: ctx.channelId,
|
|
139
|
+
timestamp,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// 2. Auto-recall before agent starts
|
|
143
|
+
api.on("before_agent_start", async (event, ctx) => {
|
|
144
|
+
const { prompt } = event;
|
|
145
|
+
if (!prompt || prompt.length < 5)
|
|
146
|
+
return;
|
|
147
|
+
try {
|
|
148
|
+
const results = await searchMemoryX(prompt, 3);
|
|
149
|
+
if (results.length === 0)
|
|
150
|
+
return;
|
|
151
|
+
const memories = results
|
|
152
|
+
.map((r) => `- [${r.category}] ${r.content}`)
|
|
153
|
+
.join("\n");
|
|
154
|
+
api.logger.info(`[MemoryX] Recalled ${results.length} memories`);
|
|
155
|
+
return {
|
|
156
|
+
prependContext: `[相关记忆]\n${memories}\n[End of memories]`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
api.logger.warn(`[MemoryX] Recall failed: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
api.logger.info("[MemoryX Realtime] Plugin registered successfully");
|
|
164
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "memoryx-realtime-plugin",
|
|
3
|
+
"name": "MemoryX Real-time Plugin",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Real-time memory capture and recall for OpenClaw",
|
|
6
|
+
"entry": "./dist/index.js",
|
|
7
|
+
"kind": "memory",
|
|
8
|
+
"configSchema": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"apiBaseUrl": {
|
|
12
|
+
"type": "string"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@t0ken.ai/memoryx-openclaw-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MemoryX real-time memory capture and recall plugin for OpenClaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"prepare": "npm run build"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["openclaw", "memoryx", "memory", "plugin"],
|
|
13
|
+
"author": "MemoryX Team",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/t0ken-ai/MemoryX.git",
|
|
18
|
+
"directory": "plugins/memoryx-realtime-plugin"
|
|
19
|
+
},
|
|
20
|
+
"openclaw": {
|
|
21
|
+
"extensions": ["./dist/index.js"]
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.0.0",
|
|
25
|
+
"@types/node": "^20.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryX Real-time Plugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* 1. Real-time message capture to MemoryX
|
|
6
|
+
* 2. Auto-recall memories before agent starts
|
|
7
|
+
* 3. Compatible with memoryx-realtime-plugin (avoids duplication)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// MemoryX API configuration
|
|
11
|
+
const MEMORYX_API_BASE = "http://t0ken.ai/api";
|
|
12
|
+
|
|
13
|
+
// Check if memoryx-realtime-plugin is installed
|
|
14
|
+
function isPluginInstalled(): boolean {
|
|
15
|
+
try {
|
|
16
|
+
const { execSync } = require("child_process");
|
|
17
|
+
const result = execSync("openclaw plugins list", {
|
|
18
|
+
encoding: "utf8",
|
|
19
|
+
timeout: 5000,
|
|
20
|
+
});
|
|
21
|
+
return (
|
|
22
|
+
result.includes("memoryx-realtime") && result.includes("loaded")
|
|
23
|
+
);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Store message to MemoryX
|
|
30
|
+
async function storeToMemoryX(
|
|
31
|
+
content: string,
|
|
32
|
+
category: string = "semantic",
|
|
33
|
+
metadata: Record<string, any> = {}
|
|
34
|
+
): Promise<boolean> {
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`${MEMORYX_API_BASE}/memories`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify({
|
|
42
|
+
content,
|
|
43
|
+
category,
|
|
44
|
+
project_id: "default",
|
|
45
|
+
metadata: {
|
|
46
|
+
...metadata,
|
|
47
|
+
source: "openclaw-realtime-plugin",
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
return response.ok;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Search MemoryX
|
|
59
|
+
async function searchMemoryX(
|
|
60
|
+
query: string,
|
|
61
|
+
limit: number = 3
|
|
62
|
+
): Promise<Array<{ id: string; content: string; category: string; similarity: number }>> {
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetch(`${MEMORYX_API_BASE}/memories/search`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
query,
|
|
71
|
+
limit,
|
|
72
|
+
project_id: "default",
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) return [];
|
|
76
|
+
const data = (await response.json()) as { data?: any[] };
|
|
77
|
+
return data.data || [];
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if content should be captured
|
|
84
|
+
function shouldCapture(text: string): boolean {
|
|
85
|
+
if (!text || text.length < 5 || text.length > 500) return false;
|
|
86
|
+
|
|
87
|
+
const skipPatterns = [
|
|
88
|
+
/^[好的ok谢谢嗯啊哈哈你好hihello拜拜再见]{1,3}$/i,
|
|
89
|
+
/^[??!!。,,\s]{1,5}$/,
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
for (const pattern of skipPatterns) {
|
|
93
|
+
if (pattern.test(text.trim())) return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const triggers = [
|
|
97
|
+
/记住|记一下|别忘了|save|remember/i,
|
|
98
|
+
/我喜欢|我讨厌|我习惯|我偏好|prefer|like|hate/i,
|
|
99
|
+
/我是|我在|我来自|i am|i work/i,
|
|
100
|
+
/纠正|更正|应该是|correct|actually/i,
|
|
101
|
+
/计划|打算|目标|plan|goal|will/i,
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
return triggers.some((pattern) => pattern.test(text));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Detect category
|
|
108
|
+
function detectCategory(text: string): string {
|
|
109
|
+
const lower = text.toLowerCase();
|
|
110
|
+
if (/prefer|like|hate|习惯|偏好|喜欢|讨厌/.test(lower)) return "preference";
|
|
111
|
+
if (/correct|纠正|更正/.test(lower)) return "correction";
|
|
112
|
+
if (/plan|goal|计划|打算/.test(lower)) return "plan";
|
|
113
|
+
return "semantic";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// OpenClaw Hook handlers
|
|
117
|
+
export async function onMessage(
|
|
118
|
+
message: string,
|
|
119
|
+
context: Record<string, any>
|
|
120
|
+
): Promise<{ context: Record<string, any> }> {
|
|
121
|
+
// Skip if memoryx-realtime-plugin is installed (avoid duplication)
|
|
122
|
+
if (isPluginInstalled()) {
|
|
123
|
+
return { context };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!shouldCapture(message)) {
|
|
127
|
+
return { context };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const category = detectCategory(message);
|
|
131
|
+
|
|
132
|
+
// Async store (non-blocking)
|
|
133
|
+
storeToMemoryX(message, category, {
|
|
134
|
+
from: context?.from,
|
|
135
|
+
channel: context?.channelId,
|
|
136
|
+
}).catch(() => {});
|
|
137
|
+
|
|
138
|
+
return { context };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function onResponse(
|
|
142
|
+
response: string,
|
|
143
|
+
context: Record<string, any>
|
|
144
|
+
): string {
|
|
145
|
+
return response;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Lifecycle hooks for OpenClaw Extension
|
|
149
|
+
export function register(api: any) {
|
|
150
|
+
api.logger.info("[MemoryX Realtime] Plugin registering...");
|
|
151
|
+
|
|
152
|
+
// 1. Message capture
|
|
153
|
+
api.on("message_received", async (event: any, ctx: any) => {
|
|
154
|
+
if (isPluginInstalled()) return;
|
|
155
|
+
|
|
156
|
+
const { content, from, timestamp } = event;
|
|
157
|
+
if (!shouldCapture(content)) return;
|
|
158
|
+
|
|
159
|
+
const category = detectCategory(content);
|
|
160
|
+
await storeToMemoryX(content, category, {
|
|
161
|
+
from,
|
|
162
|
+
channel: ctx.channelId,
|
|
163
|
+
timestamp,
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// 2. Auto-recall before agent starts
|
|
168
|
+
api.on("before_agent_start", async (event: any, ctx: any) => {
|
|
169
|
+
const { prompt } = event;
|
|
170
|
+
if (!prompt || prompt.length < 5) return;
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const results = await searchMemoryX(prompt, 3);
|
|
174
|
+
if (results.length === 0) return;
|
|
175
|
+
|
|
176
|
+
const memories = results
|
|
177
|
+
.map((r) => `- [${r.category}] ${r.content}`)
|
|
178
|
+
.join("\n");
|
|
179
|
+
|
|
180
|
+
api.logger.info(`[MemoryX] Recalled ${results.length} memories`);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
prependContext: `[相关记忆]\n${memories}\n[End of memories]`,
|
|
184
|
+
};
|
|
185
|
+
} catch (error) {
|
|
186
|
+
api.logger.warn(`[MemoryX] Recall failed: ${error}`);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
api.logger.info("[MemoryX Realtime] Plugin registered successfully");
|
|
191
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"]
|
|
17
|
+
}
|