@memoryrelay/plugin-memoryrelay-ai 0.13.0 → 0.14.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 CHANGED
@@ -1,125 +1,83 @@
1
- # MemoryRelay AI - OpenClaw Memory Plugin
1
+ # MemoryRelay AI
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@memoryrelay/plugin-memoryrelay-ai.svg)](https://www.npmjs.com/package/@memoryrelay/plugin-memoryrelay-ai)
4
- [![OpenClaw Compatible](https://img.shields.io/badge/OpenClaw-2026.2.26+-blue.svg)](https://openclaw.ai)
3
+ **Engineering Knowledge Platform for OpenClaw**
5
4
 
6
- AI-powered long-term memory for OpenClaw agents. Gives your AI assistant persistent memory, project context, architectural decision records, reusable patterns, and session tracking across conversations.
5
+ Persistent memory, architectural decisions, reusable patterns, and project orchestration for AI agents.
7
6
 
8
- ## Features
7
+ [![npm version](https://img.shields.io/npm/v/@memoryrelay/plugin-memoryrelay-ai.svg)](https://www.npmjs.com/package/@memoryrelay/plugin-memoryrelay-ai)
8
+ [![OpenClaw Compatible](https://img.shields.io/badge/OpenClaw-2026.2.26+-blue.svg)](https://openclaw.ai)
9
9
 
10
- - **39 Tools** covering memories, entities, sessions, decisions, patterns, and projects
11
- - **8 Gateway Methods** for stats, debugging, and onboarding
12
- - **Smart Auto-Capture** - Tier-based privacy system with automatic filtering
13
- - **Daily Memory Stats** - Morning/evening summaries with growth metrics
14
- - **Debug & Monitoring** - Comprehensive logging, health checks, and performance metrics
15
- - **Semantic Search** - Vector-based retrieval finds relevant context by meaning
16
- - **Auto-Recall** - Automatically injects relevant memories into agent context
17
- - **Project-First Workflow** - Agents receive workflow instructions to start with project context
18
- - **Decision Records** - Track and check architectural decisions before making new ones
19
- - **Pattern Library** - Create, search, and adopt reusable conventions across projects
20
- - **Session Tracking** - Track work sessions with summaries for continuity
21
- - **External Session IDs** - Multi-agent collaboration and conversation-spanning sessions
22
- - **Stale Session Cleanup** - Background service automatically closes inactive sessions after a configurable timeout
23
- - **Sender Identity Tagging** - Multi-agent traceability via auto-injected `sender_id` metadata
24
- - **Tool Group Filtering** - Enable only the tool groups you need
10
+ ## Why MemoryRelay?
25
11
 
26
- ## Installation
12
+ MemoryRelay is designed for engineering teams managing complex, long-running projects. It is not general-purpose Q&A memory.
27
13
 
28
- ### Requirements
14
+ | Feature | MemoryRelay | Mem0 | OpenClaw-Projects |
15
+ |---------|------------|------|-------------------|
16
+ | Semantic search | Yes (pgvector) | Yes | No |
17
+ | Sessions | Yes (auto-sync with OpenClaw sessions) | No | No |
18
+ | Architectural Decision Records | Yes (record, check, supersede) | No | No |
19
+ | Reusable patterns | Yes (create, adopt, suggest) | No | No |
20
+ | Project orchestration | Yes (10 tools, dependency graphs) | No | Basic |
21
+ | Entities / knowledge graph | Yes (create, link, graph) | Yes | No |
22
+ | Multi-agent collaboration | Yes (agent scoping, subagent tracking) | Limited | No |
23
+ | Auto-capture with privacy tiers | Yes (off/conservative/smart/aggressive) | Basic | No |
24
+ | Direct commands | 15 | ~5 | 0 |
25
+ | Lifecycle hooks | 13 | 0 | 0 |
26
+ | Tools | 39 | ~10 | 0 |
29
27
 
30
- - OpenClaw >= 2026.2.26
31
- - Node.js >= 20.0.0
32
- - MemoryRelay API key ([get one at memoryrelay.ai](https://memoryrelay.ai))
28
+ ## Quick Start
33
29
 
34
- ### Install via OpenClaw CLI
30
+ **1. Install the plugin**
35
31
 
36
32
  ```bash
37
33
  openclaw plugins install @memoryrelay/plugin-memoryrelay-ai
38
34
  ```
39
35
 
40
- ### Configuration
36
+ **2. Set your API key**
41
37
 
42
38
  ```bash
43
- openclaw config set plugins.entries.plugin-memoryrelay-ai.config '{
44
- "apiKey": "mem_prod_your_key_here",
45
- "agentId": "your-agent-name",
46
- "defaultProject": "my-project",
47
- "autoRecall": true,
48
- "autoCapture": false
49
- }'
50
-
51
- # Or use environment variables
52
39
  export MEMORYRELAY_API_KEY="mem_prod_your_key_here"
53
- export MEMORYRELAY_AGENT_ID="your-agent-name"
54
- export MEMORYRELAY_DEFAULT_PROJECT="my-project"
55
-
56
- # Restart gateway
57
- openclaw gateway restart
58
40
  ```
59
41
 
60
- ## Configuration Options
42
+ Or configure inline:
61
43
 
62
- | Option | Type | Default | Description |
63
- |--------|------|---------|-------------|
64
- | `apiKey` | string | — | MemoryRelay API key (or `MEMORYRELAY_API_KEY` env var) |
65
- | `agentId` | string | — | Unique agent identifier (or `MEMORYRELAY_AGENT_ID` env var, or agent name) |
66
- | `apiUrl` | string | `https://api.memoryrelay.net` | API endpoint (or `MEMORYRELAY_API_URL` env var) |
67
- | `defaultProject` | string | — | Default project slug applied to sessions, decisions, and memories |
68
- | `enabledTools` | string | `all` | Comma-separated tool groups: `memory`, `entity`, `agent`, `session`, `decision`, `pattern`, `project`, `health` |
69
- | `autoRecall` | boolean | `true` | Inject relevant memories into context each turn |
70
- | `autoCapture` | boolean\|object | `false` | Auto-capture config. Boolean for backward compat, object for tier system: `{enabled, tier, confirmFirst}`. Tiers: `off`, `conservative`, `smart`, `aggressive`. |
71
- | `recallLimit` | number | `5` | Max memories to inject per turn (1-20) |
72
- | `recallThreshold` | number | `0.3` | Minimum similarity score for recall (0-1) |
73
- | `excludeChannels` | string[] | `[]` | Channel IDs to skip auto-recall |
74
- | `debug` | boolean | `false` | Enable debug logging of API calls |
75
- | `verbose` | boolean | `false` | Include request/response bodies in debug logs |
76
- | `logFile` | string | — | Optional file path for persistent debug logs |
77
- | `maxLogEntries` | number | `100` | Circular buffer size for in-memory logs |
78
- | `sessionTimeoutMinutes` | number | `120` | Idle time before a session is automatically closed by the cleanup service |
79
- | `sessionCleanupIntervalMinutes` | number | `30` | How often the background cleanup service checks for stale sessions |
80
-
81
- ## Smart Auto-Capture
82
-
83
- Four capture modes with built-in privacy protection:
84
-
85
- | Tier | When to Use | Privacy Level |
86
- |------|-------------|---------------|
87
- | `off` | Manual storage only | N/A |
88
- | `conservative` | Low-risk conversations only | High (blocks most patterns) |
89
- | `smart` | Balanced automation | Medium (blocks sensitive data) |
90
- | `aggressive` | Maximum capture | Low (minimal blocking) |
44
+ ```bash
45
+ openclaw config set plugins.entries.plugin-memoryrelay-ai.config '{"apiKey": "mem_prod_..."}'
46
+ ```
91
47
 
92
- **Privacy Blocklist** — Automatically filters passwords, API keys, credit card numbers, SSNs, and other sensitive data.
48
+ **3. Verify**
93
49
 
94
- ```json
95
- {
96
- "autoCapture": {
97
- "enabled": true,
98
- "tier": "smart",
99
- "confirmFirst": 5
100
- }
101
- }
50
+ ```
51
+ /memory-health
102
52
  ```
103
53
 
104
- With `confirmFirst`, the first N captures show confirmation prompts before running silently.
54
+ Auto-recall and smart auto-capture are enabled by default. The plugin injects relevant memories into context every turn and captures important information automatically.
105
55
 
106
- ## Agent Workflow
56
+ ## Use Cases
107
57
 
108
- The plugin injects workflow instructions into every agent conversation via the `before_agent_start` hook, guiding the AI to follow a project-first approach:
58
+ **Tech Lead** managing 3+ projects:
59
+ - Record architectural decisions with `decision_record` so future agents (and teammates) check before re-deciding
60
+ - Create reusable patterns (`pattern_create`) and adopt them across projects
61
+ - Use `project_impact` to understand blast radius before cross-cutting changes
109
62
 
110
- 1. **Load context** — `project_context(project)` loads hot-tier memories, active decisions, and adopted patterns
111
- 2. **Start session** `session_start(title, project)` begins tracking work
112
- 3. **Check decisions** `decision_check(query, project)` before architectural choices
113
- 4. **Find patterns** `pattern_search(query)` to find established conventions
114
- 5. **Store findings** — `memory_store(content)` for important information
115
- 6. **Record decisions** — `decision_record(title, rationale)` for significant choices
116
- 7. **End session** — `session_end(session_id, summary)` with accomplishment summary
63
+ **DevOps Engineer**:
64
+ - Store infrastructure decisions as ADRs: "Why we chose Fargate over ECS on EC2"
65
+ - Capture runbooks and operational procedures as patterns
66
+ - Track dependencies between services with `project_add_relationship`
117
67
 
118
- For new projects, the agent is guided to call `project_register()` first.
68
+ **Solo Developer**:
69
+ - Build a personal knowledge base of memories, entities, and decisions
70
+ - Use `memory_recall` for semantic search across everything you have stored
71
+ - Link entities to memories for a navigable knowledge graph
119
72
 
120
- ## Tool Reference
73
+ **Coding Agent**:
74
+ - Auto-capture learns from conversations without explicit tool calls
75
+ - Pattern adoption ensures consistent code style across sessions
76
+ - Session tracking provides continuity when context windows reset
121
77
 
122
- ### Memory Tools (9 tools) group: `memory`
78
+ ## Features -- 39 Tools by Category
79
+
80
+ ### Memory (9 tools) -- group: `memory`
123
81
 
124
82
  | Tool | Description |
125
83
  |------|-------------|
@@ -133,7 +91,7 @@ For new projects, the agent is guided to call `project_register()` first.
133
91
  | `memory_context` | Build a token-budget-aware context window from relevant memories |
134
92
  | `memory_promote` | Update a memory's importance score and tier |
135
93
 
136
- ### Entity Tools (4 tools) group: `entity`
94
+ ### Entity (4 tools) -- group: `entity`
137
95
 
138
96
  | Tool | Description |
139
97
  |------|-------------|
@@ -142,7 +100,7 @@ For new projects, the agent is guided to call `project_register()` first.
142
100
  | `entity_list` | List entities with pagination |
143
101
  | `entity_graph` | Explore an entity's neighborhood in the knowledge graph |
144
102
 
145
- ### Agent Tools (3 tools) group: `agent`
103
+ ### Agent (3 tools) -- group: `agent`
146
104
 
147
105
  | Tool | Description |
148
106
  |------|-------------|
@@ -150,7 +108,7 @@ For new projects, the agent is guided to call `project_register()` first.
150
108
  | `agent_create` | Create a new agent (memory namespace) |
151
109
  | `agent_get` | Get agent details by ID |
152
110
 
153
- ### Session Tools (4 tools) group: `session`
111
+ ### Session (4 tools) -- group: `session`
154
112
 
155
113
  | Tool | Description |
156
114
  |------|-------------|
@@ -159,7 +117,7 @@ For new projects, the agent is guided to call `project_register()` first.
159
117
  | `session_recall` | Get session details and timeline |
160
118
  | `session_list` | List sessions filtered by project or status |
161
119
 
162
- ### Decision Tools (4 tools) group: `decision`
120
+ ### Decision (4 tools) -- group: `decision`
163
121
 
164
122
  | Tool | Description |
165
123
  |------|-------------|
@@ -168,7 +126,7 @@ For new projects, the agent is guided to call `project_register()` first.
168
126
  | `decision_supersede` | Replace a decision with a new one (old is marked superseded) |
169
127
  | `decision_check` | Semantic search for existing decisions before making new ones |
170
128
 
171
- ### Pattern Tools (4 tools) group: `pattern`
129
+ ### Pattern (4 tools) -- group: `pattern`
172
130
 
173
131
  | Tool | Description |
174
132
  |------|-------------|
@@ -177,7 +135,7 @@ For new projects, the agent is guided to call `project_register()` first.
177
135
  | `pattern_adopt` | Adopt an existing pattern for a project |
178
136
  | `pattern_suggest` | Get pattern suggestions based on project stack |
179
137
 
180
- ### Project Tools (10 tools) group: `project`
138
+ ### Project (10 tools) -- group: `project`
181
139
 
182
140
  | Tool | Description |
183
141
  |------|-------------|
@@ -192,7 +150,7 @@ For new projects, the agent is guided to call `project_register()` first.
192
150
  | `project_shared_patterns` | Find patterns shared between two projects |
193
151
  | `project_context` | Load full project context (memories, decisions, patterns, sessions) |
194
152
 
195
- ### Health Tools (1 tool) group: `health`
153
+ ### Health (1 tool) -- group: `health`
196
154
 
197
155
  | Tool | Description |
198
156
  |------|-------------|
@@ -200,145 +158,200 @@ For new projects, the agent is guided to call `project_register()` first.
200
158
 
201
159
  ## Direct Commands
202
160
 
203
- These slash commands bypass the LLM and execute immediately in the CLI:
161
+ These slash commands bypass the LLM and execute immediately.
162
+
163
+ ### Inspection Commands
204
164
 
205
165
  | Command | Description |
206
166
  |---------|-------------|
207
- | `/memory-status` | Show connection status, tool counts, and memory stats |
208
- | `/memory-stats` | Show memory growth, categories, and daily statistics |
209
- | `/memory-health` | Run health check against the MemoryRelay API |
210
- | `/memory-logs` | Show recent debug log entries |
211
- | `/memory-metrics` | Show per-tool performance metrics (call count, success rate, latency) |
167
+ | `/memory-search <query>` | Semantic search across stored memories |
168
+ | `/memory-sessions` | List sessions (optional: `active`, `closed`, or project slug) |
169
+ | `/memory-decisions` | List architectural decisions (optional: project slug) |
170
+ | `/memory-patterns` | List or search patterns (optional: search query) |
171
+ | `/memory-entities` | List entities (optional: entity type filter) |
172
+ | `/memory-projects` | List registered projects |
173
+ | `/memory-agents` | List registered agents |
212
174
 
213
- ## Tool Group Filtering
175
+ ### Diagnostic Commands
214
176
 
215
- Only enable the groups you need:
177
+ | Command | Description |
178
+ |---------|-------------|
179
+ | `/memory-status` | Connection status, tool counts, and memory stats |
180
+ | `/memory-stats` | Daily statistics (total, growth, top categories) |
181
+ | `/memory-health` | API health check with response time |
182
+ | `/memory-logs` | Recent debug log entries (optional: limit, tool filter) |
183
+ | `/memory-metrics` | Per-tool call counts, success rates, and latency |
184
+ | `/memory-validate` | Production readiness checks |
185
+ | `/memory-config` | Display current plugin configuration |
216
186
 
217
- ```json
218
- {
219
- "enabledTools": "memory,session,decision"
220
- }
221
- ```
187
+ ### Management Commands
222
188
 
223
- This enables only the memory (9), session (4), and decision (4) tools — 17 tools instead of 39. Useful for reducing tool clutter when you don't need project graphs or pattern management.
189
+ | Command | Description |
190
+ |---------|-------------|
191
+ | `/memory-forget <id>` | Delete a specific memory by ID |
224
192
 
225
- Available groups: `memory`, `entity`, `agent`, `session`, `decision`, `pattern`, `project`, `health`
193
+ ## Configuration Reference
226
194
 
227
- Set to `all` (or omit) to enable everything.
195
+ ```bash
196
+ openclaw config set plugins.entries.plugin-memoryrelay-ai.config '{
197
+ "apiKey": "mem_prod_...",
198
+ "agentId": "iris",
199
+ "defaultProject": "my-api",
200
+ "autoRecall": true,
201
+ "autoCapture": { "enabled": true, "tier": "smart", "confirmFirst": 5 }
202
+ }'
203
+ ```
228
204
 
229
- ## Auto-Recall
205
+ | Key | Type | Default | Description |
206
+ |-----|------|---------|-------------|
207
+ | `apiKey` | string | -- | MemoryRelay API key |
208
+ | `agentId` | string | -- | Unique agent identifier |
209
+ | `apiUrl` | string | `https://api.memoryrelay.net` | API endpoint |
210
+ | `defaultProject` | string | -- | Default project slug for sessions, decisions, and memories |
211
+ | `enabledTools` | string | `all` | Comma-separated tool groups to enable |
212
+ | `autoRecall` | boolean | `true` | Inject relevant memories into context each turn |
213
+ | `autoCapture` | boolean \| object | `true` | Auto-capture config (see tiers below) |
214
+ | `recallLimit` | number | `5` | Max memories injected per turn (1-20) |
215
+ | `recallThreshold` | number | `0.3` | Minimum similarity score for recall (0-1) |
216
+ | `excludeChannels` | string[] | `[]` | Channel IDs to skip auto-recall |
217
+ | `sessionTimeoutMinutes` | number | `120` | Idle time before session auto-close (10-1440) |
218
+ | `sessionCleanupIntervalMinutes` | number | `30` | Stale session check interval (5-360) |
219
+ | `debug` | boolean | `false` | Enable debug logging of API calls |
220
+ | `verbose` | boolean | `false` | Include request/response bodies in logs |
221
+ | `maxLogEntries` | number | `100` | Circular buffer size for in-memory logs (10-10000) |
230
222
 
231
- When `autoRecall: true`, relevant memories are automatically injected into agent context each turn:
223
+ ### Environment Variables
232
224
 
233
- ```
234
- User: "How should I handle authentication in this project?"
225
+ | Variable | Maps to |
226
+ |----------|---------|
227
+ | `MEMORYRELAY_API_KEY` | `apiKey` |
228
+ | `MEMORYRELAY_AGENT_ID` | `agentId` |
229
+ | `MEMORYRELAY_API_URL` | `apiUrl` |
230
+ | `MEMORYRELAY_DEFAULT_PROJECT` | `defaultProject` |
235
231
 
236
- [Plugin searches memories for "authentication"]
237
- [Injects workflow instructions + top 5 relevant memories into context]
238
- Agent uses past decisions and patterns to inform its response
239
- ```
232
+ ### Auto-Capture Tiers
240
233
 
241
- ## Channel Exclusions
234
+ | Tier | Behavior | Use When |
235
+ |------|----------|----------|
236
+ | `off` | Manual `memory_store` only | Full control, no surprises |
237
+ | `conservative` | Captures only low-risk technical facts | Sensitive environments |
238
+ | `smart` (default) | Balanced automation with privacy blocklist | Most teams |
239
+ | `aggressive` | Maximum capture, minimal filtering | Solo prototyping |
242
240
 
243
- Exclude specific channels from auto-recall and workflow injection:
241
+ The `confirmFirst` setting (default: `5`) prompts for confirmation on the first N captures before running silently. The `blocklist` array accepts regex patterns for content that should never be captured.
244
242
 
245
243
  ```json
246
244
  {
247
- "excludeChannels": [
248
- "telegram:group_123456",
249
- "discord:channel_789012"
250
- ]
245
+ "autoCapture": {
246
+ "enabled": true,
247
+ "tier": "smart",
248
+ "confirmFirst": 5,
249
+ "blocklist": ["password", "secret", "Bearer\\s+\\S+"],
250
+ "categories": {
251
+ "credentials": true,
252
+ "preferences": true,
253
+ "technical": true,
254
+ "personal": false
255
+ }
256
+ }
251
257
  }
252
258
  ```
253
259
 
254
- ## Debug & Monitoring
260
+ ## Architecture & Privacy
255
261
 
256
- ### Enable Debug Mode
262
+ ### Data Flow
257
263
 
258
- ```json
259
- {
260
- "debug": true,
261
- "verbose": false,
262
- "maxLogEntries": 1000
263
- }
264
+ ```
265
+ Agent <-> Plugin <-> MemoryRelay API (HTTPS) <-> PostgreSQL + pgvector
264
266
  ```
265
267
 
266
- ### Gateway Methods
268
+ All data in transit is encrypted via HTTPS. The plugin communicates with `api.memoryrelay.net` using bearer token authentication.
267
269
 
268
- | Method | Purpose | Example |
269
- |--------|---------|---------|
270
- | `memoryrelay.logs` | View debug logs | `openclaw gateway-call memoryrelay.logs '{"limit": 50}'` |
271
- | `memoryrelay.health` | Run health check | `openclaw gateway-call memoryrelay.health` |
272
- | `memoryrelay.test` | Test individual tools | `openclaw gateway-call memoryrelay.test '{"tool": "memory_store"}'` |
273
- | `memoryrelay.metrics` | View performance stats | `openclaw gateway-call memoryrelay.metrics` |
274
- | `memoryrelay.heartbeat` | Daily stats check | `openclaw gateway-call memoryrelay.heartbeat` |
275
- | `memoryrelay.stats` | CLI stats command | `openclaw gateway-call memoryrelay.stats '{"format": "json"}'` |
276
- | `memoryrelay.onboarding` | Show onboarding | `openclaw gateway-call memoryrelay.onboarding` |
277
- | `memory.status` | Plugin status report | `openclaw gateway-call memory.status` |
270
+ ### Privacy Controls
278
271
 
279
- ### Debug Log Format
272
+ - **Blocklist regex patterns** in auto-capture config filter passwords, API keys, credit card numbers, SSNs, and other sensitive data before storage
273
+ - **Redaction hooks** on `before_message_write` and `tool_result_persist` apply blocklist patterns to messages and tool results before persistence
274
+ - **No credential storage** by default -- the `personal` category requires explicit opt-in
275
+ - **Channel exclusions** prevent auto-recall on sensitive channels
280
276
 
281
- When debug mode is enabled, each API call is logged with timestamp, tool name, duration, and status. Verbose mode additionally captures request/response bodies for deep troubleshooting.
277
+ ### Multi-Agent Support
282
278
 
283
- ## Privacy & Security
279
+ - Each agent has its own memory namespace via `agentId`
280
+ - Projects, decisions, and patterns are shared across agents
281
+ - Subagent spawning and completion are tracked via lifecycle hooks (`subagent_spawned`, `subagent_ended`)
282
+ - Sender identity is auto-injected into memory metadata for traceability
284
283
 
285
- - **Cloud-Backed** — Memories stored on MemoryRelay servers (HTTPS-encrypted in transit)
286
- - **API Key Auth** — Bearer token authentication
287
- - **Agent Isolation** — Memories scoped per agent ID
288
- - **Channel Filtering** — Exclude sensitive channels from auto-recall
289
- - **Privacy Blocklist** — Auto-capture filters sensitive data (passwords, SSNs, credit cards, API keys)
290
- - **Privacy Redaction Hooks** — Sensitive data is also redacted from messages (`before_message_write`) and tool results (`tool_result_persist`) before persistence
291
- - **Sender Identity** — `memory_store`, `memory_batch_store`, and `decision_record` auto-inject `sender_id` from tool context into metadata for multi-agent traceability
292
- - **Never store secrets** — Do not store API keys, passwords, or tokens as memories
284
+ ### Lifecycle Hooks
293
285
 
294
- ## Troubleshooting
286
+ The plugin registers 14 lifecycle hooks:
295
287
 
296
- ### Plugin Not Loading
288
+ | Hook | Purpose |
289
+ |------|---------|
290
+ | `before_agent_start` | Auto-recall and workflow injection |
291
+ | `agent_end` | Auto-capture from completed conversations |
292
+ | `session_start` | Auto-create MemoryRelay session from OpenClaw session |
293
+ | `session_end` | Auto-end MemoryRelay session |
294
+ | `before_tool_call` | Reserved for future tool blocking/audit |
295
+ | `after_tool_call` | Session activity tracking and metrics |
296
+ | `before_compaction` | Save key context before compaction |
297
+ | `before_reset` | Save key context before session reset |
298
+ | `message_received` | Activity timestamp updates |
299
+ | `message_sending` | Reserved for future extensibility |
300
+ | `before_message_write` | Privacy redaction |
301
+ | `subagent_spawned` | Track multi-agent collaboration |
302
+ | `subagent_ended` | Store subagent completion summaries |
303
+ | `tool_result_persist` | Privacy redaction on tool results |
297
304
 
298
- ```bash
299
- # Check if installed
300
- npm list -g @memoryrelay/plugin-memoryrelay-ai
305
+ ### Skills
301
306
 
302
- # Reinstall if needed
303
- openclaw plugins install @memoryrelay/plugin-memoryrelay-ai --force
307
+ The plugin ships with 8 skills providing guided workflows on top of the raw tools:
304
308
 
305
- # Restart gateway
306
- openclaw gateway restart
309
+ - **Agent-facing**: `memory-workflow`, `decision-tracking`, `pattern-management`, `project-orchestration`, `entity-and-context`
310
+ - **Developer-facing**: `codebase-navigation`, `testing-memoryrelay`, `release-process`
307
311
 
308
- # Check logs for errors
309
- openclaw gateway logs | grep memoryrelay
310
- ```
312
+ ## Troubleshooting
311
313
 
312
- ### API Connection Issues
314
+ ### Connection refused / API key issues
313
315
 
314
316
  ```bash
315
- # Test API directly
316
- curl -H "X-API-Key: YOUR_KEY" https://api.memoryrelay.net/v1/health
317
+ # Test the API directly
318
+ curl -H "X-API-Key: $MEMORYRELAY_API_KEY" https://api.memoryrelay.net/v1/health
319
+
320
+ # Check plugin status
321
+ /memory-health
317
322
 
318
- # Check gateway logs
319
- openclaw gateway logs -f | grep memory-memoryrelay
323
+ # Run full validation
324
+ /memory-validate
320
325
  ```
321
326
 
322
- ### Auto-Recall Not Working
327
+ If `/memory-health` shows `connected: false`, verify your API key is set correctly via environment variable or config. Keys start with `mem_prod_`.
323
328
 
324
- 1. Verify `autoRecall: true` in config
325
- 2. Check memories exist: `openclaw gateway-call memory_list '{"limit": 10}'`
326
- 3. Lower `recallThreshold` (try 0.1) for more results
327
- 4. Check channel not in `excludeChannels`
329
+ ### Auto-recall not working
328
330
 
329
- ### Known Limitations
331
+ 1. Confirm `autoRecall` is `true` (it is by default)
332
+ 2. Verify memories exist: run `/memory-search test` to check
333
+ 3. Lower `recallThreshold` to `0.1` for broader matching
334
+ 4. Check your channel is not in `excludeChannels`
335
+ 5. Run `/memory-status` to see the full plugin state
336
+
337
+ ### Debug logging
330
338
 
331
- - `memory_batch_store`: May return 500 errors (use individual `memory_store` as workaround)
332
- - `memory_context`: Returns 405 Method Not Allowed (use `memory_recall` instead)
339
+ Enable debug mode to see all API calls:
333
340
 
334
- ## Skills
341
+ ```json
342
+ {
343
+ "debug": true,
344
+ "verbose": true,
345
+ "maxLogEntries": 1000
346
+ }
347
+ ```
335
348
 
336
- The plugin ships with 8 skills in `skills/` 5 agent-facing and 3 developer-facing:
349
+ Then inspect with `/memory-logs` or `/memory-metrics` to identify slow or failing calls.
337
350
 
338
- - **Agent-facing**: `memory-workflow`, `decision-tracking`, `pattern-management`, `project-orchestration`, `entity-and-context`
339
- - **Developer-facing**: `codebase-navigation`, `testing-memoryrelay`, `release-process`
351
+ ### Known Limitations
340
352
 
341
- Skills are loaded via OpenClaw's skill system and provide guided workflows on top of the raw tools.
353
+ - `memory_batch_store`: May return 500 errors on large batches (use individual `memory_store` as workaround)
354
+ - `memory_context`: Returns 405 Method Not Allowed on some API versions (use `memory_recall` instead)
342
355
 
343
356
  ## Development
344
357
 
@@ -347,17 +360,14 @@ git clone https://github.com/memoryrelay/openclaw-plugin.git
347
360
  cd openclaw-plugin
348
361
  npm install
349
362
  npm test
350
- npm run test:watch
351
- npm run test:coverage
352
363
  ```
353
364
 
354
- ## License
355
-
356
- MIT License - see [LICENSE](./LICENSE) file
357
-
358
365
  ## Links
359
366
 
360
367
  - **MemoryRelay**: https://memoryrelay.ai
361
368
  - **OpenClaw**: https://docs.openclaw.ai
362
369
  - **Repository**: https://github.com/memoryrelay/openclaw-plugin
363
- - **Issues**: https://github.com/memoryrelay/openclaw-plugin/issues
370
+
371
+ ## License
372
+
373
+ MIT
package/index.ts CHANGED
@@ -4586,7 +4586,65 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
4586
4586
  });
4587
4587
 
4588
4588
  // ========================================================================
4589
- // Direct Commands (5 total) — bypass LLM, execute immediately
4589
+ // Command Argument Parser (v0.14.0)
4590
+ // ========================================================================
4591
+
4592
+ function parseCommandArgs(input: string | undefined): { positional: string[]; flags: Record<string, string | boolean> } {
4593
+ const positional: string[] = [];
4594
+ const flags: Record<string, string | boolean> = {};
4595
+
4596
+ if (!input || input.trim() === "") {
4597
+ return { positional, flags };
4598
+ }
4599
+
4600
+ const tokens: string[] = [];
4601
+ let current = "";
4602
+ let inQuote: string | null = null;
4603
+
4604
+ for (const ch of input) {
4605
+ if (inQuote) {
4606
+ if (ch === inQuote) {
4607
+ inQuote = null;
4608
+ } else {
4609
+ current += ch;
4610
+ }
4611
+ } else if (ch === '"' || ch === "'") {
4612
+ inQuote = ch;
4613
+ } else if (ch === " " || ch === "\t") {
4614
+ if (current) {
4615
+ tokens.push(current);
4616
+ current = "";
4617
+ }
4618
+ } else {
4619
+ current += ch;
4620
+ }
4621
+ }
4622
+ if (current) tokens.push(current);
4623
+
4624
+ let i = 0;
4625
+ while (i < tokens.length) {
4626
+ const token = tokens[i];
4627
+ if (token.startsWith("--")) {
4628
+ const key = token.slice(2);
4629
+ const next = tokens[i + 1];
4630
+ if (next && !next.startsWith("--")) {
4631
+ flags[key] = next;
4632
+ i += 2;
4633
+ } else {
4634
+ flags[key] = true;
4635
+ i += 1;
4636
+ }
4637
+ } else {
4638
+ positional.push(token);
4639
+ i += 1;
4640
+ }
4641
+ }
4642
+
4643
+ return { positional, flags };
4644
+ }
4645
+
4646
+ // ========================================================================
4647
+ // Direct Commands (15 total) — bypass LLM, execute immediately
4590
4648
  // ========================================================================
4591
4649
 
4592
4650
  // /memory-status — Show full plugin status report
@@ -4789,6 +4847,438 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
4789
4847
  },
4790
4848
  });
4791
4849
 
4850
+ // ========================================================================
4851
+ // Direct Commands (10 new — v0.14.0)
4852
+ // ========================================================================
4853
+
4854
+ // /memory-search — Semantic memory search
4855
+ api.registerCommand?.({
4856
+ name: "memory-search",
4857
+ description: "Semantic search across stored memories",
4858
+ requireAuth: true,
4859
+ acceptsArgs: true,
4860
+ handler: async (ctx) => {
4861
+ try {
4862
+ const { positional, flags } = parseCommandArgs(ctx.args);
4863
+ const query = positional[0];
4864
+ if (!query) {
4865
+ return { text: "Usage: /memory-search <query> [--limit 10] [--project slug] [--threshold 0.3]" };
4866
+ }
4867
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 10;
4868
+ const threshold = flags["threshold"] ? parseFloat(String(flags["threshold"])) : 0.3;
4869
+ const project = flags["project"] ? String(flags["project"]) : undefined;
4870
+
4871
+ const results = await client.search(query, limit, threshold, { project });
4872
+ const items: unknown[] = Array.isArray(results) ? results : (results as { data?: unknown[] }).data ?? [];
4873
+
4874
+ if (items.length === 0) {
4875
+ return { text: `No memories found for: "${query}"` };
4876
+ }
4877
+
4878
+ const lines: string[] = [`Memory Search: "${query}"`, "━".repeat(60)];
4879
+ for (const item of items) {
4880
+ const m = item as Record<string, unknown>;
4881
+ const content = String(m["content"] ?? "").slice(0, 120);
4882
+ const score = typeof m["similarity"] === "number" ? `${Math.round(m["similarity"] as number * 100)}%` : "N/A";
4883
+ const category = String(m["category"] ?? "general");
4884
+ const date = m["created_at"] ? new Date(String(m["created_at"])).toLocaleDateString() : "unknown";
4885
+ const id = String(m["id"] ?? "");
4886
+ lines.push(`[${score}] ${content}`);
4887
+ lines.push(` Category: ${category} | Date: ${date} | ID: ${id}`);
4888
+ }
4889
+
4890
+ return { text: lines.join("\n") };
4891
+ } catch (err) {
4892
+ return { text: `Error: ${String(err)}`, isError: true };
4893
+ }
4894
+ },
4895
+ });
4896
+
4897
+ // /memory-validate — Production readiness check
4898
+ api.registerCommand?.({
4899
+ name: "memory-validate",
4900
+ description: "Run production readiness checks for the MemoryRelay plugin",
4901
+ requireAuth: true,
4902
+ handler: async (_ctx) => {
4903
+ try {
4904
+ const results: Array<{ label: string; status: "PASS" | "FAIL" | "WARN"; detail: string }> = [];
4905
+
4906
+ // 1. API connectivity
4907
+ try {
4908
+ await client.health();
4909
+ results.push({ label: "API connectivity", status: "PASS", detail: "Health endpoint reachable" });
4910
+ } catch (err) {
4911
+ results.push({ label: "API connectivity", status: "FAIL", detail: String(err) });
4912
+ }
4913
+
4914
+ // 2. API health status
4915
+ try {
4916
+ const h = await client.health();
4917
+ const statusStr = String(h.status).toLowerCase();
4918
+ if (VALID_HEALTH_STATUSES.includes(statusStr)) {
4919
+ results.push({ label: "API health", status: "PASS", detail: `Status: ${h.status}` });
4920
+ } else {
4921
+ results.push({ label: "API health", status: "WARN", detail: `Unexpected status: ${h.status}` });
4922
+ }
4923
+ } catch (err) {
4924
+ results.push({ label: "API health", status: "FAIL", detail: String(err) });
4925
+ }
4926
+
4927
+ // 3. Core tools
4928
+ const allTools = Object.values(TOOL_GROUPS).flat();
4929
+ const coreTools = ["memory_store", "memory_recall", "memory_list"];
4930
+ const missing = coreTools.filter((t) => !allTools.includes(t));
4931
+ if (missing.length === 0) {
4932
+ results.push({ label: "Core tools", status: "PASS", detail: "memory_store, memory_recall, memory_list present" });
4933
+ } else {
4934
+ results.push({ label: "Core tools", status: "FAIL", detail: `Missing: ${missing.join(", ")}` });
4935
+ }
4936
+
4937
+ // 4. Auto-recall enabled
4938
+ const autoRecall = cfg?.autoRecall ?? true;
4939
+ results.push({
4940
+ label: "Auto-recall enabled",
4941
+ status: autoRecall ? "PASS" : "WARN",
4942
+ detail: autoRecall ? "Enabled" : "Disabled in config",
4943
+ });
4944
+
4945
+ // 5. Auto-capture enabled
4946
+ results.push({
4947
+ label: "Auto-capture enabled",
4948
+ status: autoCaptureConfig.enabled ? "PASS" : "WARN",
4949
+ detail: autoCaptureConfig.enabled ? `Enabled (tier: ${autoCaptureConfig.tier})` : "Disabled in config",
4950
+ });
4951
+
4952
+ // 6. Memory storage
4953
+ try {
4954
+ await client.list(1);
4955
+ results.push({ label: "Memory storage", status: "PASS", detail: "Storage accessible" });
4956
+ } catch (err) {
4957
+ results.push({ label: "Memory storage", status: "FAIL", detail: String(err) });
4958
+ }
4959
+
4960
+ // 7. Agent ID configured
4961
+ const agentIdOk = agentId && agentId !== "" && agentId !== "default";
4962
+ results.push({
4963
+ label: "Agent ID configured",
4964
+ status: agentIdOk ? "PASS" : "WARN",
4965
+ detail: agentIdOk ? `ID: ${agentId}` : `Agent ID is "${agentId}" — consider setting a unique ID`,
4966
+ });
4967
+
4968
+ const passes = results.filter((r) => r.status === "PASS").length;
4969
+ const failures = results.filter((r) => r.status === "FAIL").length;
4970
+ let grade: string;
4971
+ if (passes === 7) grade = "A+";
4972
+ else if (passes === 6) grade = "A";
4973
+ else if (passes === 5) grade = "B+";
4974
+ else if (passes === 4) grade = "B";
4975
+ else grade = "F";
4976
+
4977
+ const lines: string[] = ["MemoryRelay Production Readiness", "━".repeat(50)];
4978
+ for (const r of results) {
4979
+ lines.push(`[${r.status.padEnd(4)}] ${r.label}: ${r.detail}`);
4980
+ }
4981
+ lines.push("─".repeat(50));
4982
+ lines.push(`Checks passed: ${passes}/7 | Grade: ${grade} | Production ready: ${failures === 0 ? "Yes" : "No"}`);
4983
+
4984
+ return { text: lines.join("\n") };
4985
+ } catch (err) {
4986
+ return { text: `Error: ${String(err)}`, isError: true };
4987
+ }
4988
+ },
4989
+ });
4990
+
4991
+ // /memory-config — Read-only config display
4992
+ api.registerCommand?.({
4993
+ name: "memory-config",
4994
+ description: "Display current MemoryRelay plugin configuration",
4995
+ requireAuth: true,
4996
+ handler: async (_ctx) => {
4997
+ try {
4998
+ const lines: string[] = ["MemoryRelay Configuration", "━".repeat(50)];
4999
+ lines.push(`API URL: ${apiUrl}`);
5000
+ lines.push(`Agent ID: ${agentId}`);
5001
+ lines.push(`Default Project: ${defaultProject || "(none)"}`);
5002
+ lines.push(`Enabled Tools: ${cfg?.enabledTools ?? "all"}`);
5003
+ lines.push(`Auto-Recall: ${cfg?.autoRecall ?? true}`);
5004
+ lines.push(`Auto-Capture: ${autoCaptureConfig.enabled} (tier: ${autoCaptureConfig.tier})`);
5005
+ lines.push(`Recall Limit: ${cfg?.recallLimit ?? 5}`);
5006
+ lines.push(`Recall Threshold: ${cfg?.recallThreshold ?? 0.3}`);
5007
+ lines.push(`Exclude Channels: ${(cfg?.excludeChannels ?? []).join(", ") || "(none)"}`);
5008
+ lines.push(`Session Timeout: ${cfg?.sessionTimeoutMinutes ?? 120} min`);
5009
+ lines.push(`Cleanup Interval: ${cfg?.sessionCleanupIntervalMinutes ?? 30} min`);
5010
+ lines.push(`Debug: ${cfg?.debug ?? false}`);
5011
+ lines.push(`Verbose: ${cfg?.verbose ?? false}`);
5012
+ lines.push(`Max Log Entries: ${cfg?.maxLogEntries ?? 100}`);
5013
+ return { text: lines.join("\n") };
5014
+ } catch (err) {
5015
+ return { text: `Error: ${String(err)}`, isError: true };
5016
+ }
5017
+ },
5018
+ });
5019
+
5020
+ // /memory-sessions — List sessions
5021
+ api.registerCommand?.({
5022
+ name: "memory-sessions",
5023
+ description: "List MemoryRelay sessions",
5024
+ requireAuth: true,
5025
+ acceptsArgs: true,
5026
+ handler: async (ctx) => {
5027
+ try {
5028
+ const { flags } = parseCommandArgs(ctx.args);
5029
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 10;
5030
+ const project = flags["project"] ? String(flags["project"]) : undefined;
5031
+ let status: string | undefined = flags["status"] ? String(flags["status"]) : undefined;
5032
+ if (flags["active"]) status = "active";
5033
+
5034
+ const raw = await client.listSessions(limit, project, status);
5035
+ const sessions: unknown[] = Array.isArray(raw) ? raw : (raw as { data?: unknown[] }).data ?? [];
5036
+
5037
+ if (sessions.length === 0) {
5038
+ return { text: "No sessions found." };
5039
+ }
5040
+
5041
+ const lines: string[] = ["MemoryRelay Sessions", "━".repeat(60)];
5042
+ for (const session of sessions) {
5043
+ const s = session as Record<string, unknown>;
5044
+ const sid = String(s["id"] ?? "");
5045
+ const sessionStatus = String(s["status"] ?? "unknown").toUpperCase();
5046
+ const startedAt = s["started_at"] ? new Date(String(s["started_at"])).toLocaleString() : "unknown";
5047
+ let duration = "ongoing";
5048
+ if (s["started_at"] && s["ended_at"]) {
5049
+ const diffMs = new Date(String(s["ended_at"])).getTime() - new Date(String(s["started_at"])).getTime();
5050
+ const diffMin = Math.round(diffMs / 60000);
5051
+ duration = `${diffMin}m`;
5052
+ }
5053
+ const summary = String(s["summary"] ?? "").slice(0, 80);
5054
+ lines.push(`[${sessionStatus}] ${sid}`);
5055
+ lines.push(` Started: ${startedAt} | Duration: ${duration}`);
5056
+ if (summary) lines.push(` ${summary}`);
5057
+ }
5058
+
5059
+ return { text: lines.join("\n") };
5060
+ } catch (err) {
5061
+ return { text: `Error: ${String(err)}`, isError: true };
5062
+ }
5063
+ },
5064
+ });
5065
+
5066
+ // /memory-decisions — List decisions
5067
+ api.registerCommand?.({
5068
+ name: "memory-decisions",
5069
+ description: "List architectural decisions stored in MemoryRelay",
5070
+ requireAuth: true,
5071
+ acceptsArgs: true,
5072
+ handler: async (ctx) => {
5073
+ try {
5074
+ const { flags } = parseCommandArgs(ctx.args);
5075
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 10;
5076
+ const project = flags["project"] ? String(flags["project"]) : undefined;
5077
+ const status = flags["status"] ? String(flags["status"]) : undefined;
5078
+ const tags = flags["tags"] ? String(flags["tags"]) : undefined;
5079
+
5080
+ const raw = await client.listDecisions(limit, project, status, tags);
5081
+ const decisions: unknown[] = Array.isArray(raw) ? raw : (raw as { data?: unknown[] }).data ?? [];
5082
+
5083
+ if (decisions.length === 0) {
5084
+ return { text: "No decisions found." };
5085
+ }
5086
+
5087
+ const lines: string[] = ["MemoryRelay Decisions", "━".repeat(60)];
5088
+ for (const decision of decisions) {
5089
+ const d = decision as Record<string, unknown>;
5090
+ const decisionStatus = String(d["status"] ?? "unknown").toUpperCase();
5091
+ const title = String(d["title"] ?? "(untitled)");
5092
+ const date = d["created_at"] ? new Date(String(d["created_at"])).toLocaleDateString() : "unknown";
5093
+ const rationale = String(d["rationale"] ?? "").slice(0, 100);
5094
+ lines.push(`[${decisionStatus}] ${title} (${date})`);
5095
+ if (rationale) lines.push(` ${rationale}`);
5096
+ }
5097
+
5098
+ return { text: lines.join("\n") };
5099
+ } catch (err) {
5100
+ return { text: `Error: ${String(err)}`, isError: true };
5101
+ }
5102
+ },
5103
+ });
5104
+
5105
+ // /memory-patterns — List/search patterns
5106
+ api.registerCommand?.({
5107
+ name: "memory-patterns",
5108
+ description: "List or search memory patterns",
5109
+ requireAuth: true,
5110
+ acceptsArgs: true,
5111
+ handler: async (ctx) => {
5112
+ try {
5113
+ const { positional, flags } = parseCommandArgs(ctx.args);
5114
+ const query = positional[0] ?? "";
5115
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 10;
5116
+ const category = flags["category"] ? String(flags["category"]) : undefined;
5117
+ const project = flags["project"] ? String(flags["project"]) : undefined;
5118
+
5119
+ const raw = await client.searchPatterns(query, category, project, limit);
5120
+ const patterns: unknown[] = Array.isArray(raw) ? raw : (raw as { data?: unknown[] }).data ?? [];
5121
+
5122
+ if (patterns.length === 0) {
5123
+ return { text: query ? `No patterns found for: "${query}"` : "No patterns found." };
5124
+ }
5125
+
5126
+ const lines: string[] = ["MemoryRelay Patterns", "━".repeat(60)];
5127
+ for (const pattern of patterns) {
5128
+ const p = pattern as Record<string, unknown>;
5129
+ const name = String(p["name"] ?? "(unnamed)");
5130
+ const cat = String(p["category"] ?? "general");
5131
+ const description = String(p["description"] ?? "").slice(0, 100);
5132
+ lines.push(`${name} [${cat}]`);
5133
+ if (description) lines.push(` ${description}`);
5134
+ }
5135
+
5136
+ return { text: lines.join("\n") };
5137
+ } catch (err) {
5138
+ return { text: `Error: ${String(err)}`, isError: true };
5139
+ }
5140
+ },
5141
+ });
5142
+
5143
+ // /memory-entities — List entities
5144
+ api.registerCommand?.({
5145
+ name: "memory-entities",
5146
+ description: "List entities stored in MemoryRelay",
5147
+ requireAuth: true,
5148
+ acceptsArgs: true,
5149
+ handler: async (ctx) => {
5150
+ try {
5151
+ const { flags } = parseCommandArgs(ctx.args);
5152
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 20;
5153
+
5154
+ const raw = await client.listEntities(limit);
5155
+ const entities: unknown[] = Array.isArray(raw) ? raw : (raw as { data?: unknown[] }).data ?? [];
5156
+
5157
+ if (entities.length === 0) {
5158
+ return { text: "No entities found." };
5159
+ }
5160
+
5161
+ const lines: string[] = ["MemoryRelay Entities", "━".repeat(60)];
5162
+ for (const entity of entities) {
5163
+ const e = entity as Record<string, unknown>;
5164
+ const name = String(e["name"] ?? "(unnamed)");
5165
+ const type = String(e["type"] ?? "unknown");
5166
+ const relationships = Array.isArray(e["relationships"]) ? e["relationships"].length : (typeof e["relationship_count"] === "number" ? e["relationship_count"] : 0);
5167
+ lines.push(`${name} [${type}] (${relationships} relationships)`);
5168
+ }
5169
+
5170
+ return { text: lines.join("\n") };
5171
+ } catch (err) {
5172
+ return { text: `Error: ${String(err)}`, isError: true };
5173
+ }
5174
+ },
5175
+ });
5176
+
5177
+ // /memory-projects — List projects
5178
+ api.registerCommand?.({
5179
+ name: "memory-projects",
5180
+ description: "List projects in MemoryRelay",
5181
+ requireAuth: true,
5182
+ acceptsArgs: true,
5183
+ handler: async (ctx) => {
5184
+ try {
5185
+ const { flags } = parseCommandArgs(ctx.args);
5186
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 20;
5187
+
5188
+ const raw = await client.listProjects(limit);
5189
+ const projects: unknown[] = Array.isArray(raw) ? raw : (raw as { data?: unknown[] }).data ?? [];
5190
+
5191
+ if (projects.length === 0) {
5192
+ return { text: "No projects found." };
5193
+ }
5194
+
5195
+ const lines: string[] = ["MemoryRelay Projects", "━".repeat(60)];
5196
+ for (const project of projects) {
5197
+ const p = project as Record<string, unknown>;
5198
+ const slug = String(p["slug"] ?? "(no-slug)");
5199
+ const description = String(p["description"] ?? "").slice(0, 80);
5200
+ const memoryCount = typeof p["memory_count"] === "number" ? p["memory_count"] : 0;
5201
+ lines.push(`${slug} — ${description || "(no description)"} (${memoryCount} memories)`);
5202
+ }
5203
+
5204
+ return { text: lines.join("\n") };
5205
+ } catch (err) {
5206
+ return { text: `Error: ${String(err)}`, isError: true };
5207
+ }
5208
+ },
5209
+ });
5210
+
5211
+ // /memory-agents — List agents
5212
+ api.registerCommand?.({
5213
+ name: "memory-agents",
5214
+ description: "List agents registered in MemoryRelay",
5215
+ requireAuth: true,
5216
+ acceptsArgs: true,
5217
+ handler: async (ctx) => {
5218
+ try {
5219
+ const { flags } = parseCommandArgs(ctx.args);
5220
+ const limit = flags["limit"] ? parseInt(String(flags["limit"]), 10) : 20;
5221
+
5222
+ const raw = await client.listAgents(limit);
5223
+ const agents: unknown[] = Array.isArray(raw) ? raw : (raw as { data?: unknown[] }).data ?? [];
5224
+
5225
+ if (agents.length === 0) {
5226
+ return { text: "No agents found." };
5227
+ }
5228
+
5229
+ const lines: string[] = ["MemoryRelay Agents", "━".repeat(60)];
5230
+ for (const agent of agents) {
5231
+ const a = agent as Record<string, unknown>;
5232
+ const id = String(a["id"] ?? "(no-id)");
5233
+ const name = String(a["name"] ?? "");
5234
+ const description = String(a["description"] ?? "");
5235
+ lines.push(`${id}${name ? ` (${name})` : ""}${description ? `, ${description}` : ""}`);
5236
+ }
5237
+
5238
+ return { text: lines.join("\n") };
5239
+ } catch (err) {
5240
+ return { text: `Error: ${String(err)}`, isError: true };
5241
+ }
5242
+ },
5243
+ });
5244
+
5245
+ // /memory-forget — Delete a memory by ID
5246
+ api.registerCommand?.({
5247
+ name: "memory-forget",
5248
+ description: "Delete a specific memory by ID",
5249
+ requireAuth: true,
5250
+ acceptsArgs: true,
5251
+ handler: async (ctx) => {
5252
+ const { positional } = parseCommandArgs(ctx.args);
5253
+ const memoryId = positional[0];
5254
+ if (!memoryId) {
5255
+ return { text: "Usage: /memory-forget <memory-id>" };
5256
+ }
5257
+ try {
5258
+ let preview = "";
5259
+ try {
5260
+ const existing = await client.get(memoryId);
5261
+ const m = existing as Record<string, unknown>;
5262
+ preview = String(m["content"] ?? "").slice(0, 120);
5263
+ } catch (_) {
5264
+ // preview unavailable — proceed with delete
5265
+ }
5266
+
5267
+ await client.delete(memoryId);
5268
+
5269
+ const lines = [`Memory deleted: ${memoryId}`];
5270
+ if (preview) lines.push(`Content: ${preview}`);
5271
+ return { text: lines.join("\n") };
5272
+ } catch (err) {
5273
+ const msg = String(err);
5274
+ if (msg.toLowerCase().includes("not found") || msg.includes("404")) {
5275
+ return { text: `Memory not found: ${memoryId}`, isError: true };
5276
+ }
5277
+ return { text: `Error: ${msg}`, isError: true };
5278
+ }
5279
+ },
5280
+ });
5281
+
4792
5282
  // ========================================================================
4793
5283
  // Stale Session Cleanup Service (v0.13.0)
4794
5284
  // ========================================================================
@@ -2,8 +2,8 @@
2
2
  "id": "plugin-memoryrelay-ai",
3
3
  "kind": "memory",
4
4
  "name": "MemoryRelay AI",
5
- "description": "MemoryRelay v0.13.0 - Long-term memory with sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
- "version": "0.13.0",
5
+ "description": "MemoryRelay v0.14.0 - Long-term memory with 15 commands, sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
+ "version": "0.14.0",
7
7
  "uiHints": {
8
8
  "apiKey": {
9
9
  "label": "MemoryRelay API Key",
@@ -57,6 +57,32 @@
57
57
  "placeholder": "30",
58
58
  "advanced": true,
59
59
  "help": "How often to check for stale sessions (default: 30)"
60
+ },
61
+ "recallLimit": {
62
+ "label": "Recall Limit",
63
+ "placeholder": "5",
64
+ "help": "Maximum number of memories to inject per auto-recall (1-20)"
65
+ },
66
+ "recallThreshold": {
67
+ "label": "Recall Threshold",
68
+ "placeholder": "0.3",
69
+ "help": "Minimum similarity score for auto-recall (0-1, lower = more results)"
70
+ },
71
+ "debug": {
72
+ "label": "Debug Logging",
73
+ "help": "Enable debug logging of API calls",
74
+ "advanced": true
75
+ },
76
+ "verbose": {
77
+ "label": "Verbose Logging",
78
+ "help": "Include request/response bodies in debug logs (requires debug: true)",
79
+ "advanced": true
80
+ },
81
+ "maxLogEntries": {
82
+ "label": "Max Log Entries",
83
+ "placeholder": "100",
84
+ "help": "Circular buffer size for in-memory debug logs (10-10,000)",
85
+ "advanced": true
60
86
  }
61
87
  },
62
88
  "configSchema": {
@@ -86,8 +112,34 @@
86
112
  "description": "Comma-separated tool groups to enable: memory, entity, agent, session, decision, pattern, project, health, or 'all' (default: all)"
87
113
  },
88
114
  "autoCapture": {
89
- "type": "boolean",
90
- "default": false
115
+ "oneOf": [
116
+ { "type": "boolean" },
117
+ {
118
+ "type": "object",
119
+ "properties": {
120
+ "enabled": { "type": "boolean", "default": true },
121
+ "tier": { "type": "string", "enum": ["off", "conservative", "smart", "aggressive"], "default": "smart" },
122
+ "confirmFirst": { "type": "number", "default": 5, "minimum": 0, "description": "Number of captures to confirm before auto-accepting" },
123
+ "categories": {
124
+ "type": "object",
125
+ "properties": {
126
+ "credentials": { "type": "boolean", "default": true },
127
+ "preferences": { "type": "boolean", "default": true },
128
+ "technical": { "type": "boolean", "default": true },
129
+ "personal": { "type": "boolean", "default": false, "description": "Privacy: personal info requires confirmation" }
130
+ }
131
+ },
132
+ "blocklist": {
133
+ "type": "array",
134
+ "items": { "type": "string" },
135
+ "default": [],
136
+ "description": "Regex patterns for content to never capture"
137
+ }
138
+ }
139
+ }
140
+ ],
141
+ "default": true,
142
+ "description": "Auto-capture configuration. Use true/false for simple toggle, or object for fine-grained control."
91
143
  },
92
144
  "autoRecall": {
93
145
  "type": "boolean",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memoryrelay/plugin-memoryrelay-ai",
3
- "version": "0.13.0",
4
- "description": "OpenClaw memory plugin for MemoryRelay API - sessions, decisions, patterns, projects & semantic search",
3
+ "version": "0.14.0",
4
+ "description": "OpenClaw memory plugin for MemoryRelay API - 15 commands, sessions, decisions, patterns, projects & semantic search",
5
5
  "type": "module",
6
6
  "main": "index.ts",
7
7
  "scripts": {