@ynhcj/xiaoyi-channel 0.0.191-beta → 0.0.192-beta
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.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleMemoryQueryEvent(context: any, cfg: any): Promise<void>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Memory query event handler.
|
|
2
|
+
// Listens for memory-query-event from the WebSocket manager,
|
|
3
|
+
// handles memory state read/write and MEMORY.md/USER.md file queries.
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
7
|
+
import { sendCommand } from "./formatter.js";
|
|
8
|
+
import { resolveXYConfig } from "./config.js";
|
|
9
|
+
import { logger } from "./utils/logger.js";
|
|
10
|
+
const XIAOYIRUNTIME_PATH_PRIMARY = "/home/sandbox/.openclaw/.xiaoyiruntime";
|
|
11
|
+
const XIAOYIRUNTIME_PATH_FALLBACK = `${os.homedir()}/.openclaw/.xiaoyiruntime`;
|
|
12
|
+
const MEMORY_STATE_KEY = "MEMORYSTATE";
|
|
13
|
+
/** Resolve writable .xiaoyiruntime path: try sandbox path first, fallback to user home. */
|
|
14
|
+
function resolveXiaoyiRuntimePath() {
|
|
15
|
+
try {
|
|
16
|
+
// If primary path's parent dir exists and is writable, use it
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
fs.accessSync(XIAOYIRUNTIME_PATH_PRIMARY, fs.constants.W_OK);
|
|
19
|
+
return XIAOYIRUNTIME_PATH_PRIMARY;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// If primary path doesn't exist, try creating parent
|
|
23
|
+
try {
|
|
24
|
+
const fs = require("fs");
|
|
25
|
+
const dir = require("path").dirname(XIAOYIRUNTIME_PATH_PRIMARY);
|
|
26
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
27
|
+
return XIAOYIRUNTIME_PATH_PRIMARY;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Fallback to user home
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return XIAOYIRUNTIME_PATH_FALLBACK;
|
|
34
|
+
}
|
|
35
|
+
export async function handleMemoryQueryEvent(context, cfg) {
|
|
36
|
+
const { action, params, sessionId, taskId, messageId } = context;
|
|
37
|
+
const log = logger.withContext(sessionId ?? "", taskId ?? "");
|
|
38
|
+
log.log(`[MEMORY-QUERY] Received event: action=${action}`);
|
|
39
|
+
let result;
|
|
40
|
+
try {
|
|
41
|
+
switch (action) {
|
|
42
|
+
case "memoryStateSet":
|
|
43
|
+
result = handleMemoryStateSet(params);
|
|
44
|
+
break;
|
|
45
|
+
case "userMdQuery":
|
|
46
|
+
result = handleUserMdQuery();
|
|
47
|
+
break;
|
|
48
|
+
case "memoryMdQuery":
|
|
49
|
+
result = handleMemoryMdQuery();
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
log.error(`[MEMORY-QUERY] Unknown action: ${action}`);
|
|
53
|
+
result = { error: `Unknown action: ${action}` };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
58
|
+
log.error(`[MEMORY-QUERY] Handler failed for action=${action}:`, err);
|
|
59
|
+
result = { error: errorMsg };
|
|
60
|
+
}
|
|
61
|
+
log.log(`[MEMORY-QUERY] Result for action=${action}: ${JSON.stringify(result)}`);
|
|
62
|
+
// Send result back via sendCommand
|
|
63
|
+
if (cfg && sessionId && taskId && messageId) {
|
|
64
|
+
try {
|
|
65
|
+
const config = resolveXYConfig(cfg);
|
|
66
|
+
const command = {
|
|
67
|
+
header: {
|
|
68
|
+
namespace: "AgentEvent",
|
|
69
|
+
name: "memoryQuery",
|
|
70
|
+
},
|
|
71
|
+
payload: {
|
|
72
|
+
action,
|
|
73
|
+
ans: result,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
await sendCommand({
|
|
77
|
+
config,
|
|
78
|
+
sessionId,
|
|
79
|
+
taskId,
|
|
80
|
+
messageId,
|
|
81
|
+
command,
|
|
82
|
+
final: true,
|
|
83
|
+
});
|
|
84
|
+
log.log(`[MEMORY-QUERY] Sent response via sendCommand, action=${action}`);
|
|
85
|
+
}
|
|
86
|
+
catch (sendErr) {
|
|
87
|
+
log.error(`[MEMORY-QUERY] Failed to send response via sendCommand:`, sendErr);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
log.warn(`[MEMORY-QUERY] Missing cfg/sessionId/taskId/messageId, skipping sendCommand`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Write MEMORYSTATE=true/false to .xiaoyiruntime.
|
|
96
|
+
*/
|
|
97
|
+
function handleMemoryStateSet(params) {
|
|
98
|
+
const memoryState = params?.memoryState;
|
|
99
|
+
if (typeof memoryState !== "boolean") {
|
|
100
|
+
logger.error(`[MEMORY-QUERY] memoryStateSet: invalid memoryState type: ${typeof memoryState}`);
|
|
101
|
+
return { code: 0 };
|
|
102
|
+
}
|
|
103
|
+
const value = String(memoryState);
|
|
104
|
+
const filePath = resolveXiaoyiRuntimePath();
|
|
105
|
+
let content;
|
|
106
|
+
try {
|
|
107
|
+
content = readFileSync(filePath, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
logger.log(`[MEMORY-QUERY] ${filePath} not found, creating new file`);
|
|
111
|
+
writeFileSync(filePath, `${MEMORY_STATE_KEY}=${value}\n`, "utf-8");
|
|
112
|
+
logger.log(`[MEMORY-QUERY] wrote ${MEMORY_STATE_KEY}=${value}`);
|
|
113
|
+
return { code: 0 };
|
|
114
|
+
}
|
|
115
|
+
const lines = content.split("\n");
|
|
116
|
+
const key = MEMORY_STATE_KEY;
|
|
117
|
+
let found = false;
|
|
118
|
+
const updated = lines.map((line) => {
|
|
119
|
+
if (line.startsWith(`${key}=`)) {
|
|
120
|
+
found = true;
|
|
121
|
+
return `${key}=${value}`;
|
|
122
|
+
}
|
|
123
|
+
return line;
|
|
124
|
+
});
|
|
125
|
+
if (!found) {
|
|
126
|
+
const trimmed = content.trimEnd();
|
|
127
|
+
writeFileSync(filePath, `${trimmed}\n${key}=${value}\n`, "utf-8");
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
writeFileSync(filePath, updated.join("\n"), "utf-8");
|
|
131
|
+
}
|
|
132
|
+
logger.log(`[MEMORY-QUERY] updated ${MEMORY_STATE_KEY}=${value} in ${filePath}`);
|
|
133
|
+
return { code: 0 };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Read ~/.openclaw/workspace/USER.md and return content in fileDetail.
|
|
137
|
+
*/
|
|
138
|
+
function handleUserMdQuery() {
|
|
139
|
+
const filePath = path.join(os.homedir(), ".openclaw", "workspace", "USER.md");
|
|
140
|
+
return readMdFile(filePath);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Read ~/.openclaw/workspace/MEMORY.md and return content in fileDetail.
|
|
144
|
+
*/
|
|
145
|
+
function handleMemoryMdQuery() {
|
|
146
|
+
const filePath = path.join(os.homedir(), ".openclaw", "workspace", "MEMORY.md");
|
|
147
|
+
return readMdFile(filePath);
|
|
148
|
+
}
|
|
149
|
+
function readMdFile(filePath) {
|
|
150
|
+
try {
|
|
151
|
+
const content = readFileSync(filePath, "utf-8");
|
|
152
|
+
logger.log(`[MEMORY-QUERY] Read file: ${filePath}, size: ${content.length}`);
|
|
153
|
+
return { fileDetail: content };
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
if (err.code === "ENOENT") {
|
|
157
|
+
logger.log(`[MEMORY-QUERY] File not found: ${filePath}`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
logger.error(`[MEMORY-QUERY] Failed to read ${filePath}:`, err);
|
|
161
|
+
}
|
|
162
|
+
return { fileDetail: "" };
|
|
163
|
+
}
|
|
164
|
+
}
|
package/dist/src/monitor.js
CHANGED
|
@@ -8,6 +8,7 @@ import { handleTriggerEvent } from "./trigger-handler.js";
|
|
|
8
8
|
import { handleSelfEvolutionEvent, handleSelfEvolutionStateGetEvent } from "./self-evolution-handler.js";
|
|
9
9
|
import { handleLoginTokenEvent } from "./login-token-handler.js";
|
|
10
10
|
import { handleCronQueryEvent } from "./cron-query-handler.js";
|
|
11
|
+
import { handleMemoryQueryEvent } from "./memory-query-handler.js";
|
|
11
12
|
import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
|
|
12
13
|
import { cleanupStaleSessions, getActiveSessionCount, cleanupAllSessions } from "./tools/session-manager.js";
|
|
13
14
|
import { logger } from "./utils/logger.js";
|
|
@@ -205,6 +206,12 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
205
206
|
logger.error(`[MONITOR] Failed to handle cron-query-event:`, err);
|
|
206
207
|
});
|
|
207
208
|
};
|
|
209
|
+
const memoryQueryEventHandler = (context) => {
|
|
210
|
+
logger.log(`[MONITOR] Received memory-query-event, dispatching to handler...`);
|
|
211
|
+
handleMemoryQueryEvent(context, cfg).catch((err) => {
|
|
212
|
+
logger.error(`[MONITOR] Failed to handle memory-query-event:`, err);
|
|
213
|
+
});
|
|
214
|
+
};
|
|
208
215
|
const cleanup = () => {
|
|
209
216
|
logger.log("XY gateway: cleaning up...");
|
|
210
217
|
// 🔍 Diagnose before cleanup
|
|
@@ -226,6 +233,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
226
233
|
wsManager.off("self-evolution-state-get-event", selfEvolutionStateGetHandler);
|
|
227
234
|
wsManager.off("login-token-event", loginTokenEventHandler);
|
|
228
235
|
wsManager.off("cron-query-event", cronQueryEventHandler);
|
|
236
|
+
wsManager.off("memory-query-event", memoryQueryEventHandler);
|
|
229
237
|
// ✅ Disconnect the wsManager to prevent connection leaks
|
|
230
238
|
// This is safe because each gateway lifecycle should have clean connections
|
|
231
239
|
wsManager.disconnect();
|
|
@@ -290,6 +298,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
290
298
|
wsManager.on("self-evolution-state-get-event", selfEvolutionStateGetHandler);
|
|
291
299
|
wsManager.on("login-token-event", loginTokenEventHandler);
|
|
292
300
|
wsManager.on("cron-query-event", cronQueryEventHandler);
|
|
301
|
+
wsManager.on("memory-query-event", memoryQueryEventHandler);
|
|
293
302
|
// Start periodic health check (every 6 hours)
|
|
294
303
|
logger.log("Starting periodic health check (every 6 hours)...");
|
|
295
304
|
healthCheckInterval = setInterval(() => {
|
package/dist/src/websocket.js
CHANGED
|
@@ -630,6 +630,15 @@ export class XYWebSocketManager extends EventEmitter {
|
|
|
630
630
|
messageId: a2aRequest.id,
|
|
631
631
|
});
|
|
632
632
|
}
|
|
633
|
+
else if (item.header?.namespace === "AgentEvent" && item.header?.name === "memoryQuery") {
|
|
634
|
+
log.log("[XY] AgentEvent.memoryQuery detected, emitting memory-query-event");
|
|
635
|
+
this.emit("memory-query-event", {
|
|
636
|
+
...(item.payload ?? {}),
|
|
637
|
+
sessionId,
|
|
638
|
+
taskId: a2aRequest.params?.id,
|
|
639
|
+
messageId: a2aRequest.id,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
633
642
|
else if (item.header?.namespace === "System" && item.header?.name === "ExecuteAgentAsSkillResponse") {
|
|
634
643
|
log.log("[XY] ExecuteAgentAsSkillResponse detected, emitting agent-as-skill-response");
|
|
635
644
|
this.emit("agent-as-skill-response", item);
|
|
@@ -719,6 +728,15 @@ export class XYWebSocketManager extends EventEmitter {
|
|
|
719
728
|
messageId: a2aRequest.id,
|
|
720
729
|
});
|
|
721
730
|
}
|
|
731
|
+
else if (item.header?.namespace === "AgentEvent" && item.header?.name === "memoryQuery") {
|
|
732
|
+
log.log("[XY] AgentEvent.memoryQuery detected (wrapped format), emitting memory-query-event");
|
|
733
|
+
this.emit("memory-query-event", {
|
|
734
|
+
...(item.payload ?? {}),
|
|
735
|
+
sessionId: inboundMsg.sessionId || a2aRequest.params?.sessionId,
|
|
736
|
+
taskId: inboundMsg.taskId || a2aRequest.params?.id,
|
|
737
|
+
messageId: a2aRequest.id,
|
|
738
|
+
});
|
|
739
|
+
}
|
|
722
740
|
else if (item.header?.namespace === "System" && item.header?.name === "ExecuteAgentAsSkillResponse") {
|
|
723
741
|
log.log("[XY] ExecuteAgentAsSkillResponse detected (wrapped format), emitting agent-as-skill-response");
|
|
724
742
|
this.emit("agent-as-skill-response", item);
|