@ynhcj/xiaoyi-channel 0.0.192-beta → 0.0.194-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.
@@ -39,15 +39,21 @@ export async function handleMemoryQueryEvent(context, cfg) {
39
39
  let result;
40
40
  try {
41
41
  switch (action) {
42
- case "memoryStateSet":
42
+ case "MemoryStateSet":
43
43
  result = handleMemoryStateSet(params);
44
44
  break;
45
- case "userMdQuery":
45
+ case "MemoryStateGet":
46
+ result = handleMemoryStateGet();
47
+ break;
48
+ case "UserMdQuery":
46
49
  result = handleUserMdQuery();
47
50
  break;
48
- case "memoryMdQuery":
51
+ case "MemoryMdQuery":
49
52
  result = handleMemoryMdQuery();
50
53
  break;
54
+ case "MemoryHistory":
55
+ result = handleMemoryHistory();
56
+ break;
51
57
  default:
52
58
  log.error(`[MEMORY-QUERY] Unknown action: ${action}`);
53
59
  result = { error: `Unknown action: ${action}` };
@@ -66,7 +72,7 @@ export async function handleMemoryQueryEvent(context, cfg) {
66
72
  const command = {
67
73
  header: {
68
74
  namespace: "AgentEvent",
69
- name: "memoryQuery",
75
+ name: "MemoryQuery",
70
76
  },
71
77
  payload: {
72
78
  action,
@@ -132,6 +138,35 @@ function handleMemoryStateSet(params) {
132
138
  logger.log(`[MEMORY-QUERY] updated ${MEMORY_STATE_KEY}=${value} in ${filePath}`);
133
139
  return { code: 0 };
134
140
  }
141
+ /**
142
+ * Read MEMORYSTATE from .xiaoyiruntime and return its boolean value.
143
+ * Missing file or key defaults to false.
144
+ */
145
+ function handleMemoryStateGet() {
146
+ const filePath = resolveXiaoyiRuntimePath();
147
+ let content;
148
+ try {
149
+ content = readFileSync(filePath, "utf-8");
150
+ }
151
+ catch (err) {
152
+ if (err.code === "ENOENT") {
153
+ logger.log(`[MEMORY-QUERY] ${filePath} not found`);
154
+ }
155
+ else {
156
+ logger.error(`[MEMORY-QUERY] Failed to read ${filePath}:`, err);
157
+ }
158
+ return { memoryState: false };
159
+ }
160
+ for (const line of content.split("\n")) {
161
+ if (line.startsWith(`${MEMORY_STATE_KEY}=`)) {
162
+ const value = line.slice(`${MEMORY_STATE_KEY}=`.length).trim();
163
+ logger.log(`[MEMORY-QUERY] read ${MEMORY_STATE_KEY}=${value} from ${filePath}`);
164
+ return { memoryState: value === "true" };
165
+ }
166
+ }
167
+ logger.log(`[MEMORY-QUERY] ${MEMORY_STATE_KEY} not found in ${filePath}`);
168
+ return { memoryState: false };
169
+ }
135
170
  /**
136
171
  * Read ~/.openclaw/workspace/USER.md and return content in fileDetail.
137
172
  */
@@ -162,3 +197,86 @@ function readMdFile(filePath) {
162
197
  return { fileDetail: "" };
163
198
  }
164
199
  }
200
+ const MEMORY_LOG_PATH = path.join(os.homedir(), ".openclaw", ".memory.log");
201
+ const MEMORY_HISTORY_DAYS = 7;
202
+ const MEMORY_RETENTION_DAYS = 30;
203
+ /**
204
+ * Read ~/.openclaw/.memory.log, return last 7 days grouped by date,
205
+ * then prune entries older than 30 days.
206
+ *
207
+ * Log line format: `2026-06-22T15:18:00|user.md|更新了xxxx`
208
+ * Only split on the first two `|`; everything after is the detail
209
+ * (detail itself may contain `|`).
210
+ */
211
+ function handleMemoryHistory() {
212
+ let content;
213
+ try {
214
+ content = readFileSync(MEMORY_LOG_PATH, "utf-8");
215
+ }
216
+ catch (err) {
217
+ if (err.code === "ENOENT") {
218
+ logger.log(`[MEMORY-QUERY] memory.log not found: ${MEMORY_LOG_PATH}`);
219
+ }
220
+ else {
221
+ logger.error(`[MEMORY-QUERY] Failed to read memory.log:`, err);
222
+ }
223
+ return [];
224
+ }
225
+ const lines = content.split("\n");
226
+ const now = new Date();
227
+ const historySince = new Date(now);
228
+ historySince.setDate(now.getDate() - (MEMORY_HISTORY_DAYS - 1));
229
+ historySince.setHours(0, 0, 0, 0);
230
+ const retentionSince = new Date(now);
231
+ retentionSince.setDate(now.getDate() - (MEMORY_RETENTION_DAYS - 1));
232
+ retentionSince.setHours(0, 0, 0, 0);
233
+ const byDate = new Map();
234
+ const keptLines = [];
235
+ for (const raw of lines) {
236
+ const line = raw.trimEnd();
237
+ if (!line)
238
+ continue;
239
+ // Split on only the first two `|`; rest is detail (may contain `|`).
240
+ const firstPipe = line.indexOf("|");
241
+ if (firstPipe === -1)
242
+ continue;
243
+ const secondPipe = line.indexOf("|", firstPipe + 1);
244
+ if (secondPipe === -1)
245
+ continue;
246
+ const timestamp = line.slice(0, firstPipe);
247
+ const fileName = line.slice(firstPipe + 1, secondPipe);
248
+ const detail = line.slice(secondPipe + 1);
249
+ // timestamp format: 2026-06-22T15:18:00
250
+ const datePart = timestamp.slice(0, 10);
251
+ const timePart = timestamp.slice(11, 19);
252
+ const entryDate = new Date(`${datePart}T00:00:00`);
253
+ // Retain log lines within the 30-day window.
254
+ if (!isNaN(entryDate.getTime()) && entryDate >= retentionSince) {
255
+ keptLines.push(line);
256
+ }
257
+ // Include in response if within the 7-day window.
258
+ if (!isNaN(entryDate.getTime()) && entryDate >= historySince) {
259
+ let bucket = byDate.get(datePart);
260
+ if (!bucket) {
261
+ bucket = [];
262
+ byDate.set(datePart, bucket);
263
+ }
264
+ bucket.push({ fileName, detail, time: timePart });
265
+ }
266
+ }
267
+ // Build ans array sorted by date ascending, each entry is { <date>: [...] }.
268
+ const ans = Array.from(byDate.keys())
269
+ .sort()
270
+ .map((dateStr) => ({ [dateStr]: byDate.get(dateStr) }));
271
+ // Prune memory.log: keep only the last 30 days.
272
+ try {
273
+ const newContent = keptLines.length > 0 ? `${keptLines.join("\n")}\n` : "";
274
+ writeFileSync(MEMORY_LOG_PATH, newContent, "utf-8");
275
+ logger.log(`[MEMORY-QUERY] Pruned memory.log, kept ${keptLines.length} entries (>= ${retentionSince.toISOString().slice(0, 10)})`);
276
+ }
277
+ catch (err) {
278
+ logger.error(`[MEMORY-QUERY] Failed to prune memory.log:`, err);
279
+ }
280
+ logger.log(`[MEMORY-QUERY] MemoryHistory: returning ${ans.length} date buckets`);
281
+ return ans;
282
+ }
@@ -630,8 +630,8 @@ 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");
633
+ else if (item.header?.namespace === "AgentEvent" && item.header?.name === "MemoryQuery") {
634
+ log.log("[XY] AgentEvent.MemoryQuery detected, emitting memory-query-event");
635
635
  this.emit("memory-query-event", {
636
636
  ...(item.payload ?? {}),
637
637
  sessionId,
@@ -728,8 +728,8 @@ export class XYWebSocketManager extends EventEmitter {
728
728
  messageId: a2aRequest.id,
729
729
  });
730
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");
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
733
  this.emit("memory-query-event", {
734
734
  ...(item.payload ?? {}),
735
735
  sessionId: inboundMsg.sessionId || a2aRequest.params?.sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.192-beta",
3
+ "version": "0.0.194-beta",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",