@memoryrelay/plugin-memoryrelay-ai 0.11.1 → 0.11.3

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.
Files changed (2) hide show
  1. package/index.ts +283 -12
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * OpenClaw Memory Plugin - MemoryRelay
3
- * Version: 0.11.1 (Full Single-File)
3
+ * Version: 0.11.3 (Full Single-File)
4
4
  *
5
5
  * Long-term memory with vector search using MemoryRelay API.
6
6
  * Provides auto-recall and auto-capture via lifecycle hooks.
@@ -12,6 +12,16 @@
12
12
 
13
13
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
14
14
 
15
+ // ============================================================================
16
+ // Constants
17
+ // ============================================================================
18
+
19
+ const DEFAULT_API_URL = "https://api.memoryrelay.net";
20
+ const VALID_HEALTH_STATUSES = ["ok", "healthy", "up"];
21
+ const REQUEST_TIMEOUT_MS = 30000; // 30 seconds
22
+ const MAX_RETRIES = 3;
23
+ const INITIAL_RETRY_DELAY_MS = 1000; // 1 second
24
+
15
25
  // ============================================================================
16
26
  // DebugLogger (Inlined from src/debug-logger.ts)
17
27
  // ============================================================================
@@ -91,23 +101,284 @@ class DebugLogger {
91
101
  // StatusReporter (Inlined from src/status-reporter.ts)
92
102
  // ============================================================================
93
103
 
94
- class StatusReporter {
95
- constructor(private debugLogger?: DebugLogger) {}
104
+ * including connection status, tool breakdown, and recent activity.
105
+ */
106
+
107
+ import type { LogEntry, DebugLogger } from "./debug-logger";
108
+
109
+ export interface ToolStatus {
110
+ enabled: number;
111
+ available: number;
112
+ failed: number;
113
+ tools: {
114
+ name: string;
115
+ status: "working" | "error" | "unknown";
116
+ error?: string;
117
+ lastSuccess?: string;
118
+ lastError?: string;
119
+ }[];
120
+ }
121
+
122
+ export interface ConnectionStatus {
123
+ status: "connected" | "disconnected" | "degraded";
124
+ endpoint: string;
125
+ lastCheck: string;
126
+ responseTime: number;
127
+ }
128
+
129
+ export interface MemoryStats {
130
+ total_memories: number;
131
+ memories_today?: number;
132
+ last_stored?: string;
133
+ search_count_24h?: number;
134
+ }
135
+
136
+ export interface PluginConfig {
137
+ agentId: string;
138
+ autoRecall: boolean;
139
+ autoCapture: boolean;
140
+ recallLimit: number;
141
+ recallThreshold: number;
142
+ excludeChannels: string[];
143
+ defaultProject?: string;
144
+ }
145
+
146
+ export interface StatusReport {
147
+ connection: ConnectionStatus;
148
+ config: PluginConfig;
149
+ stats: MemoryStats;
150
+ tools: Record<string, ToolStatus>;
151
+ recentCalls: LogEntry[];
152
+ issues: { tool: string; error: string; since: string }[];
153
+ }
154
+
155
+ export class StatusReporter {
156
+ private debugLogger?: DebugLogger;
157
+ private toolFailures: Map<string, { error: string; since: string }> = new Map();
158
+
159
+ constructor(debugLogger?: DebugLogger) {
160
+ this.debugLogger = debugLogger;
161
+ }
162
+
163
+ /**
164
+ * Record tool failure
165
+ */
166
+ recordFailure(toolName: string, error: string): void {
167
+ if (!this.toolFailures.has(toolName)) {
168
+ this.toolFailures.set(toolName, {
169
+ error,
170
+ since: new Date().toISOString(),
171
+ });
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Record tool success (clears failure)
177
+ */
178
+ recordSuccess(toolName: string): void {
179
+ this.toolFailures.delete(toolName);
180
+ }
181
+
182
+ /**
183
+ * Get known issues
184
+ */
185
+ getIssues(): { tool: string; error: string; since: string }[] {
186
+ return Array.from(this.toolFailures.entries()).map(([tool, data]) => ({
187
+ tool,
188
+ error: data.error,
189
+ since: data.since,
190
+ }));
191
+ }
192
+
193
+ /**
194
+ * Build status report
195
+ */
196
+ buildReport(
197
+ connection: ConnectionStatus,
198
+ config: PluginConfig,
199
+ stats: MemoryStats,
200
+ toolGroups: Record<string, string[]>
201
+ ): StatusReport {
202
+ const recentCalls = this.debugLogger
203
+ ? this.debugLogger.getRecentLogs(10)
204
+ : [];
205
+
206
+ const tools: Record<string, ToolStatus> = {};
207
+
208
+ for (const [group, toolNames] of Object.entries(toolGroups)) {
209
+ const toolStatuses = toolNames.map(name => {
210
+ const logs = this.debugLogger?.getToolLogs(name, 1) || [];
211
+ const lastLog = logs[0];
212
+ const failure = this.toolFailures.get(name);
213
+
214
+ let status: "working" | "error" | "unknown" = "unknown";
215
+ let error: string | undefined;
216
+ let lastSuccess: string | undefined;
217
+ let lastError: string | undefined;
218
+
219
+ if (lastLog) {
220
+ status = lastLog.status === "success" ? "working" : "error";
221
+ if (lastLog.status === "success") {
222
+ lastSuccess = lastLog.timestamp;
223
+ } else {
224
+ lastError = lastLog.timestamp;
225
+ error = lastLog.error;
226
+ }
227
+ } else if (failure) {
228
+ status = "error";
229
+ error = failure.error;
230
+ lastError = failure.since;
231
+ }
232
+
233
+ return {
234
+ name,
235
+ status,
236
+ error,
237
+ lastSuccess,
238
+ lastError,
239
+ };
240
+ });
241
+
242
+ const available = toolStatuses.filter(t => t.status === "working").length;
243
+ const failed = toolStatuses.filter(t => t.status === "error").length;
244
+
245
+ tools[group] = {
246
+ enabled: toolNames.length,
247
+ available,
248
+ failed,
249
+ tools: toolStatuses,
250
+ };
251
+ }
96
252
 
97
- buildReport(connectionStatus: any, config: any, stats: any, toolGroups: any) {
98
- const report = {
99
- available: true,
100
- connected: connectionStatus.connected,
101
- apiVersion: connectionStatus.apiVersion,
253
+ return {
254
+ connection,
102
255
  config,
103
256
  stats,
104
- tools: toolGroups,
257
+ tools,
258
+ recentCalls,
259
+ issues: this.getIssues(),
105
260
  };
106
- return report;
107
261
  }
108
262
 
109
- static formatReport(report: any): string {
110
- return JSON.stringify(report, null, 2);
263
+ /**
264
+ * Format status report for CLI display
265
+ */
266
+ static formatReport(report: StatusReport): string {
267
+ const lines: string[] = [];
268
+
269
+ // Header
270
+ lines.push("");
271
+ lines.push("MemoryRelay Plugin Status");
272
+ lines.push("━".repeat(50));
273
+ lines.push("");
274
+
275
+ // Connection
276
+ lines.push("CONNECTION");
277
+ const connSymbol = report.connection.status === "connected" ? "✓" : "✗";
278
+ lines.push(` Status: ${connSymbol} ${report.connection.status}`);
279
+ lines.push(` Endpoint: ${report.connection.endpoint}`);
280
+ lines.push(` Response Time: ${report.connection.responseTime}ms`);
281
+ lines.push(` Last Check: ${new Date(report.connection.lastCheck).toLocaleString()}`);
282
+ lines.push("");
283
+
284
+ // Configuration
285
+ lines.push("CONFIGURATION");
286
+ lines.push(` Agent ID: ${report.config.agentId}`);
287
+ const recallStatus = report.config.autoRecall
288
+ ? `✓ Enabled (limit: ${report.config.recallLimit}, threshold: ${report.config.recallThreshold})`
289
+ : "✗ Disabled";
290
+ lines.push(` Auto-Recall: ${recallStatus}`);
291
+ lines.push(` Auto-Capture: ${report.config.autoCapture ? "✓ Enabled" : "✗ Disabled"}`);
292
+ if (report.config.defaultProject) {
293
+ lines.push(` Default Project: ${report.config.defaultProject}`);
294
+ }
295
+ lines.push("");
296
+
297
+ // Memory Statistics
298
+ lines.push("MEMORY STATISTICS");
299
+ lines.push(` Total Memories: ${report.stats.total_memories}`);
300
+ if (report.stats.memories_today !== undefined) {
301
+ lines.push(` Today: ${report.stats.memories_today}`);
302
+ }
303
+ if (report.stats.last_stored) {
304
+ const lastStored = new Date(report.stats.last_stored);
305
+ const ago = this.formatTimeAgo(lastStored);
306
+ lines.push(` Last Stored: ${ago}`);
307
+ }
308
+ if (report.stats.search_count_24h !== undefined) {
309
+ lines.push(` Searches (24h): ${report.stats.search_count_24h}`);
310
+ }
311
+ lines.push("");
312
+
313
+ // Tools Status
314
+ const totalEnabled = Object.values(report.tools).reduce((sum, g) => sum + g.enabled, 0);
315
+ const totalAvailable = Object.values(report.tools).reduce((sum, g) => sum + g.available, 0);
316
+ lines.push(`TOOLS STATUS (${totalAvailable}/${totalEnabled} working)`);
317
+
318
+ for (const [groupName, group] of Object.entries(report.tools)) {
319
+ const symbol = group.failed === 0 ? "✓" : group.failed === group.enabled ? "✗" : "⚠";
320
+ const label = groupName.charAt(0).toUpperCase() + groupName.slice(1);
321
+ lines.push(` ${symbol} ${label}: ${group.available}/${group.enabled} working`);
322
+
323
+ // Show failed tools
324
+ const failedTools = group.tools.filter(t => t.status === "error");
325
+ for (const tool of failedTools) {
326
+ lines.push(` ✗ ${tool.name} (${tool.error})`);
327
+ }
328
+ }
329
+ lines.push("");
330
+
331
+ // Recent Activity
332
+ if (report.recentCalls.length > 0) {
333
+ lines.push(`RECENT ACTIVITY (last ${report.recentCalls.length} calls)`);
334
+ for (const call of report.recentCalls.reverse()) {
335
+ const time = new Date(call.timestamp).toLocaleTimeString();
336
+ const status = call.status === "success" ? "✓" : "✗";
337
+ const duration = `${call.duration}ms`;
338
+ lines.push(` ${time} ${call.tool.padEnd(18)} ${duration.padStart(6)} ${status}`);
339
+ }
340
+ lines.push("");
341
+ }
342
+
343
+ // Known Issues
344
+ if (report.issues.length > 0) {
345
+ lines.push(`KNOWN ISSUES (${report.issues.length})`);
346
+ for (const issue of report.issues) {
347
+ const since = this.formatTimeAgo(new Date(issue.since));
348
+ lines.push(` ⚠ ${issue.tool} - ${issue.error} (since ${since})`);
349
+ }
350
+ lines.push("");
351
+ }
352
+
353
+ // Footer
354
+ lines.push("For detailed logs, run: openclaw memoryrelay logs");
355
+ lines.push("For troubleshooting: https://github.com/MemoryRelay/api/issues/213");
356
+ lines.push("");
357
+
358
+ return lines.join("\n");
359
+ }
360
+
361
+ /**
362
+ * Format time ago string
363
+ */
364
+ private static formatTimeAgo(date: Date): string {
365
+ const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
366
+
367
+ if (seconds < 60) return `${seconds} seconds ago`;
368
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
369
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
370
+ return `${Math.floor(seconds / 86400)} days ago`;
371
+ }
372
+
373
+ /**
374
+ * Format compact status (for inline display)
375
+ */
376
+ static formatCompact(report: StatusReport): string {
377
+ const totalEnabled = Object.values(report.tools).reduce((sum, g) => sum + g.enabled, 0);
378
+ const totalAvailable = Object.values(report.tools).reduce((sum, g) => sum + g.available, 0);
379
+ const symbol = report.connection.status === "connected" ? "✓" : "✗";
380
+
381
+ return `MemoryRelay: ${symbol} ${report.connection.status}, ${totalAvailable}/${totalEnabled} tools working`;
111
382
  }
112
383
  }
113
384
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoryrelay/plugin-memoryrelay-ai",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "OpenClaw memory plugin for MemoryRelay API - sessions, decisions, patterns, projects & semantic search",
5
5
  "type": "module",
6
6
  "main": "index.ts",