@memoryrelay/plugin-memoryrelay-ai 0.15.1 → 0.15.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.
- package/README.md +6 -3
- package/index.ts +33 -15
- package/openclaw.plugin.json +2 -2
- package/package.json +1 -1
- package/skills/decision-tracking/SKILL.md +9 -8
- package/skills/entity-and-context/SKILL.md +17 -17
- package/skills/memory-workflow/SKILL.md +11 -21
- package/skills/pattern-management/SKILL.md +15 -10
- package/skills/project-orchestration/SKILL.md +12 -12
- package/src/status-reporter.ts +20 -2
- package/skills/codebase-navigation/SKILL.md +0 -105
- package/skills/release-process/SKILL.md +0 -83
- package/skills/testing-memoryrelay/SKILL.md +0 -87
package/README.md
CHANGED
|
@@ -314,10 +314,13 @@ The plugin registers 14 lifecycle hooks:
|
|
|
314
314
|
|
|
315
315
|
### Skills
|
|
316
316
|
|
|
317
|
-
The plugin ships with
|
|
317
|
+
The plugin ships with 5 skills providing guided workflows on top of the raw tools:
|
|
318
318
|
|
|
319
|
-
-
|
|
320
|
-
-
|
|
319
|
+
- `memory-workflow` — Session lifecycle, storing/retrieving memories
|
|
320
|
+
- `decision-tracking` — ADR management, checking before deciding
|
|
321
|
+
- `pattern-management` — Reusable conventions, search before create
|
|
322
|
+
- `project-orchestration` — Multi-project context loading and impact analysis
|
|
323
|
+
- `entity-and-context` — Knowledge graph, linking entities to memories
|
|
321
324
|
|
|
322
325
|
## Updating
|
|
323
326
|
|
package/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenClaw Memory Plugin - MemoryRelay
|
|
3
|
-
* Version: 0.
|
|
3
|
+
* Version: 0.15.3
|
|
4
4
|
*
|
|
5
5
|
* Long-term memory with vector search using MemoryRelay API.
|
|
6
6
|
* Provides auto-recall and auto-capture via lifecycle hooks.
|
|
7
7
|
* Includes: memories, entities, agents, sessions, decisions, patterns, projects.
|
|
8
|
+
* New in v0.15.0: V2 async API, context_build with AI-enhanced search modes
|
|
8
9
|
* New in v0.13.0: External session IDs, get-or-create sessions, multi-agent collaboration
|
|
9
10
|
* New in v0.12.7: OpenClaw session context integration for session tracking
|
|
10
11
|
* New in v0.12.0: Smart auto-capture, daily stats, CLI commands, onboarding
|
|
@@ -67,6 +68,7 @@ interface DebugLoggerConfig {
|
|
|
67
68
|
enabled: boolean;
|
|
68
69
|
verbose: boolean;
|
|
69
70
|
maxEntries: number;
|
|
71
|
+
logFile?: string; // Deprecated: File logging removed for security compliance
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
class DebugLogger {
|
|
@@ -709,7 +711,7 @@ class MemoryRelayClient {
|
|
|
709
711
|
headers: {
|
|
710
712
|
"Content-Type": "application/json",
|
|
711
713
|
Authorization: `Bearer ${this.apiKey}`,
|
|
712
|
-
"User-Agent": "openclaw-memory-memoryrelay/0.15.
|
|
714
|
+
"User-Agent": "openclaw-memory-memoryrelay/0.15.3",
|
|
713
715
|
},
|
|
714
716
|
body: body ? JSON.stringify(body) : undefined,
|
|
715
717
|
},
|
|
@@ -1666,7 +1668,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
1666
1668
|
}
|
|
1667
1669
|
|
|
1668
1670
|
// ========================================================================
|
|
1669
|
-
// Tools (
|
|
1671
|
+
// Tools (42 total)
|
|
1670
1672
|
// ========================================================================
|
|
1671
1673
|
|
|
1672
1674
|
// --------------------------------------------------------------------------
|
|
@@ -1965,7 +1967,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
1965
1967
|
}
|
|
1966
1968
|
|
|
1967
1969
|
const list = results
|
|
1968
|
-
.map((r) => `- [${r.memory.id
|
|
1970
|
+
.map((r) => `- [${r.memory.id}] ${r.memory.content.slice(0, 60)}...`)
|
|
1969
1971
|
.join("\n");
|
|
1970
1972
|
|
|
1971
1973
|
return {
|
|
@@ -2023,7 +2025,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
2023
2025
|
};
|
|
2024
2026
|
}
|
|
2025
2027
|
const formatted = memories
|
|
2026
|
-
.map((m) => `- [${m.id
|
|
2028
|
+
.map((m) => `- [${m.id}] ${m.content.slice(0, 120)}`)
|
|
2027
2029
|
.join("\n");
|
|
2028
2030
|
return {
|
|
2029
2031
|
content: [{ type: "text", text: `${memories.length} memories:\n${formatted}` }],
|
|
@@ -4457,14 +4459,30 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
4457
4459
|
const outcome = event.outcome || "unknown";
|
|
4458
4460
|
const summary = `Subagent ${event.targetSessionKey} ended: ${event.reason} (outcome: ${outcome})`;
|
|
4459
4461
|
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4462
|
+
// Only store subagent completions if autoCapture is enabled and content passes filters (#44)
|
|
4463
|
+
if (autoCaptureConfig.enabled) {
|
|
4464
|
+
if (isBlocklisted(summary, autoCaptureConfig.blocklist || [])) {
|
|
4465
|
+
api.logger.debug?.(`memory-memoryrelay: subagent completion blocklisted, skipping storage`);
|
|
4466
|
+
return;
|
|
4467
|
+
}
|
|
4466
4468
|
|
|
4467
|
-
|
|
4469
|
+
// Skip routine completion events — only store failures or unusual outcomes
|
|
4470
|
+
if (outcome === "ok" || outcome === "success") {
|
|
4471
|
+
api.logger.debug?.(`memory-memoryrelay: skipping routine subagent completion: ${summary}`);
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4475
|
+
await client.store(summary, {
|
|
4476
|
+
category: "subagent-activity",
|
|
4477
|
+
source: "subagent_ended_hook",
|
|
4478
|
+
agent: agentId,
|
|
4479
|
+
outcome,
|
|
4480
|
+
});
|
|
4481
|
+
|
|
4482
|
+
api.logger.debug?.(`memory-memoryrelay: stored subagent completion: ${summary}`);
|
|
4483
|
+
} else {
|
|
4484
|
+
api.logger.debug?.(`memory-memoryrelay: autoCapture disabled, skipping subagent completion storage`);
|
|
4485
|
+
}
|
|
4468
4486
|
} catch (err) {
|
|
4469
4487
|
api.logger.warn?.(`memory-memoryrelay: subagent_ended hook failed: ${String(err)}`);
|
|
4470
4488
|
}
|
|
@@ -4490,7 +4508,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
4490
4508
|
});
|
|
4491
4509
|
|
|
4492
4510
|
api.logger.info?.(
|
|
4493
|
-
`memory-memoryrelay: plugin v0.15.
|
|
4511
|
+
`memory-memoryrelay: plugin v0.15.3 loaded (${Object.values(TOOL_GROUPS).flat().length} tools, autoRecall: ${cfg?.autoRecall}, autoCapture: ${autoCaptureConfig.enabled ? autoCaptureConfig.tier : 'off'}, debug: ${debugEnabled})`,
|
|
4494
4512
|
);
|
|
4495
4513
|
|
|
4496
4514
|
// ========================================================================
|
|
@@ -4551,7 +4569,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
4551
4569
|
}
|
|
4552
4570
|
|
|
4553
4571
|
const formatted = logs.map((l) =>
|
|
4554
|
-
`[${new Date(l.timestamp).toISOString()}] ${l.
|
|
4572
|
+
`[${new Date(l.timestamp).toISOString()}] ${l.status.toUpperCase()} ${l.tool ?? "-"}: ${l.method} ${l.path} (${l.duration}ms)${l.error ? ` - ${l.error}` : ""}`
|
|
4555
4573
|
).join("\n");
|
|
4556
4574
|
respond(true, {
|
|
4557
4575
|
logs,
|
|
@@ -5586,7 +5604,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
|
|
|
5586
5604
|
description: "Show how to update the MemoryRelay plugin to the latest version",
|
|
5587
5605
|
requireAuth: true,
|
|
5588
5606
|
handler: async (_ctx) => {
|
|
5589
|
-
const currentVersion = "0.15.
|
|
5607
|
+
const currentVersion = "0.15.3";
|
|
5590
5608
|
const lines: string[] = [
|
|
5591
5609
|
"MemoryRelay Plugin Update",
|
|
5592
5610
|
"━".repeat(50),
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"id": "plugin-memoryrelay-ai",
|
|
3
3
|
"kind": "memory",
|
|
4
4
|
"name": "MemoryRelay AI",
|
|
5
|
-
"description": "MemoryRelay v0.15.
|
|
6
|
-
"version": "0.15.
|
|
5
|
+
"description": "MemoryRelay v0.15.3 - Long-term memory with 42 tools, 17 commands, V2 async, sessions, decisions, patterns & projects (api.memoryrelay.net)",
|
|
6
|
+
"version": "0.15.3",
|
|
7
7
|
"uiHints": {
|
|
8
8
|
"apiKey": {
|
|
9
9
|
"label": "MemoryRelay API Key",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memoryrelay/plugin-memoryrelay-ai",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "OpenClaw memory plugin for MemoryRelay API - 42 tools, 17 commands, V2 async, sessions, decisions, patterns, projects & semantic search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -5,27 +5,27 @@ description: "Use when making architectural choices, evaluating alternatives, re
|
|
|
5
5
|
|
|
6
6
|
# Decision Tracking
|
|
7
7
|
|
|
8
|
-
Decisions are **choices with rationale and alternatives considered**. Plain facts, findings, or information are memories — use `memory_store` for those.
|
|
8
|
+
Decisions are **choices with rationale and alternatives considered**. Plain facts, findings, or information are memories — use `memory_store` for those (see `memory-workflow` skill).
|
|
9
9
|
|
|
10
10
|
## Decision Tools
|
|
11
11
|
|
|
12
12
|
| Tool | Signature | Purpose |
|
|
13
13
|
|------|-----------|---------|
|
|
14
|
-
| `decision_record` | `decision_record(title, rationale, project)` | Record a new decision with reasoning |
|
|
15
|
-
| `decision_list` | `decision_list(project)` | List
|
|
16
|
-
| `decision_supersede` | `decision_supersede(old_id, new_title, new_rationale)` | Replace an outdated decision |
|
|
17
|
-
| `decision_check` | `decision_check(query, project)` |
|
|
14
|
+
| `decision_record` | `decision_record(title, rationale, alternatives?, project?, tags?, status?)` | Record a new decision with reasoning |
|
|
15
|
+
| `decision_list` | `decision_list(project?, status?, tags?, limit?)` | List decisions, optionally filtered |
|
|
16
|
+
| `decision_supersede` | `decision_supersede(old_id, new_title, new_rationale, tags?)` | Replace an outdated decision |
|
|
17
|
+
| `decision_check` | `decision_check(query, project?, limit?, threshold?)` | Semantic search for conflicting decisions |
|
|
18
18
|
|
|
19
19
|
## Workflow
|
|
20
20
|
|
|
21
21
|
1. **Check first** — Always call `decision_check(query, project)` before making an architectural choice. This surfaces existing decisions that may conflict or already cover the topic.
|
|
22
|
-
2. **Record with rationale** — Use `decision_record(title, rationale, project)`.
|
|
22
|
+
2. **Record with rationale** — Use `decision_record(title, rationale, alternatives, project, tags)`. Include why this option was chosen, what alternatives were rejected, and tags for categorization.
|
|
23
23
|
3. **Supersede, don't duplicate** — When a decision changes, call `decision_supersede(old_id, new_title, new_rationale)`. This preserves history while marking the old decision as replaced.
|
|
24
|
-
4. **Review periodically** — Use `decision_list(project)` to audit active decisions during planning or refactoring.
|
|
24
|
+
4. **Review periodically** — Use `decision_list(project, status)` to audit active decisions during planning or refactoring. Filter by `status: "active"` to see current decisions only.
|
|
25
25
|
|
|
26
26
|
## Project Scoping
|
|
27
27
|
|
|
28
|
-
Decisions must be scoped to the relevant project:
|
|
28
|
+
Decisions must be scoped to the relevant project (see `project-orchestration` skill for project setup):
|
|
29
29
|
|
|
30
30
|
- Set `defaultProject` in plugin config to avoid passing `project` on every call.
|
|
31
31
|
- Pass `project` explicitly when working across multiple projects.
|
|
@@ -48,3 +48,4 @@ Decisions must be scoped to the relevant project:
|
|
|
48
48
|
| Adding a new decision when one already exists | Use `decision_supersede` to replace the old decision, preserving history |
|
|
49
49
|
| Omitting the `project` parameter | Scope every decision to a project via parameter or `defaultProject` config |
|
|
50
50
|
| Storing facts as decisions | Only choices with rationale belong in decisions; use `memory_store` for information |
|
|
51
|
+
| Not using `tags` | Tags enable filtering with `decision_list(project, tags: "api,auth")` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: entity-and-context
|
|
3
|
-
description: "Use when building relationship maps between people,
|
|
3
|
+
description: "Use when building relationship maps between people, organizations, projects, or concepts that appear across multiple memories, or when recalling information that benefits from entity connections rather than flat search."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Entity and Context
|
|
@@ -11,22 +11,22 @@ Entities turn flat memory storage into a connected knowledge graph. Create entit
|
|
|
11
11
|
|
|
12
12
|
| Tool | Signature | Purpose |
|
|
13
13
|
|------|-----------|---------|
|
|
14
|
-
| `entity_create` | `entity_create(name, type,
|
|
15
|
-
| `entity_link` | `entity_link(entity_id, memory_id)` | Connect an entity to a memory |
|
|
16
|
-
| `entity_list` | `entity_list(
|
|
17
|
-
| `entity_graph` | `entity_graph(entity_id)` |
|
|
18
|
-
| `memory_context` | `memory_context(query)` | Enriched recall with entity connections (memory
|
|
14
|
+
| `entity_create` | `entity_create(name, type, metadata?)` | Create a new entity node |
|
|
15
|
+
| `entity_link` | `entity_link(entity_id, memory_id, relationship?)` | Connect an entity to a memory |
|
|
16
|
+
| `entity_list` | `entity_list(limit?, offset?)` | List entities with pagination |
|
|
17
|
+
| `entity_graph` | `entity_graph(entity_id, depth?, max_neighbors?)` | Explore an entity's neighborhood |
|
|
18
|
+
| `memory_context` | `memory_context(query, token_budget?)` | Enriched recall with entity connections (see `memory-workflow` skill) |
|
|
19
19
|
|
|
20
20
|
## Entity Types
|
|
21
21
|
|
|
22
22
|
| Type | Examples |
|
|
23
23
|
|------|----------|
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
24
|
+
| `person` | Team members, stakeholders, external contacts |
|
|
25
|
+
| `place` | Data centers, offices, deployment regions |
|
|
26
|
+
| `organization` | Companies, departments, vendors |
|
|
27
|
+
| `project` | Codebases, initiatives, products |
|
|
28
|
+
| `concept` | Architecture patterns, business domains, protocols |
|
|
29
|
+
| `other` | Anything that doesn't fit the above |
|
|
30
30
|
|
|
31
31
|
## When to Create Entities
|
|
32
32
|
|
|
@@ -37,11 +37,11 @@ Create an entity when a named thing:
|
|
|
37
37
|
|
|
38
38
|
## Linking Workflow
|
|
39
39
|
|
|
40
|
-
1. **Create the entity** — `entity_create("AuthService", "
|
|
41
|
-
2. **Store related memories** — `memory_store(content, metadata)` as usual
|
|
42
|
-
3. **Link them** — `entity_link(entity_id, memory_id)` to connect entity to each relevant memory
|
|
40
|
+
1. **Create the entity** — `entity_create("AuthService", "concept", { "domain": "security" })`
|
|
41
|
+
2. **Store related memories** — `memory_store(content, metadata)` as usual (see `memory-workflow` skill)
|
|
42
|
+
3. **Link them** — `entity_link(entity_id, memory_id, "mentioned_in")` to connect entity to each relevant memory
|
|
43
43
|
4. **Query with context** — `memory_context("authentication flow")` returns memories enriched with linked entity data
|
|
44
|
-
5. **Explore connections** — `entity_graph(entity_id)` to see
|
|
44
|
+
5. **Explore connections** — `entity_graph(entity_id, 2, 10)` to see related memories and co-linked entities up to 2 hops away
|
|
45
45
|
|
|
46
46
|
## memory_context vs memory_recall
|
|
47
47
|
|
|
@@ -58,5 +58,5 @@ Create an entity when a named thing:
|
|
|
58
58
|
| Creating entities for one-off mentions | Only create entities for things referenced across multiple memories |
|
|
59
59
|
| Not linking entities to memories | An unlinked entity is invisible to `memory_context`; always link after creating |
|
|
60
60
|
| Using `memory_recall` when entities exist | Switch to `memory_context` for richer results that include entity connections |
|
|
61
|
-
| Creating duplicate entities | Call `entity_list(
|
|
61
|
+
| Creating duplicate entities | Call `entity_list()` first to check if the entity already exists |
|
|
62
62
|
| Linking everything to one entity | Keep links specific; an entity should connect only to directly relevant memories |
|
|
@@ -13,23 +13,27 @@ Follow this order every time. Skipping steps causes orphaned memories.
|
|
|
13
13
|
|------|------|---------|
|
|
14
14
|
| 1 | `project_context(project)` | Load hot-tier memories, active decisions, adopted patterns |
|
|
15
15
|
| 2 | `session_start(title, project)` | Begin tracking work (returns `session_id`) |
|
|
16
|
-
| 3 | `decision_check(query, project)` | Check existing decisions before architectural choices |
|
|
17
|
-
| 4 | `pattern_search(query)` | Find established conventions |
|
|
16
|
+
| 3 | `decision_check(query, project)` | Check existing decisions before architectural choices (see `decision-tracking` skill) |
|
|
17
|
+
| 4 | `pattern_search(query)` | Find established conventions (see `pattern-management` skill) |
|
|
18
18
|
|
|
19
19
|
## During Work
|
|
20
20
|
|
|
21
21
|
| Action | Tool | Notes |
|
|
22
22
|
|--------|------|-------|
|
|
23
23
|
| Save info | `memory_store(content, metadata)` | Always set `deduplicate=true` |
|
|
24
|
-
| Search | `memory_recall(query)` | Semantic search across memories |
|
|
24
|
+
| Search | `memory_recall(query, limit?, threshold?)` | Semantic search across memories |
|
|
25
25
|
| Delete | `memory_forget(id_or_query)` | By ID or fuzzy search |
|
|
26
26
|
| Browse | `memory_list(limit, offset)` | Chronological listing |
|
|
27
27
|
| Read one | `memory_get(id)` | Fetch by exact ID |
|
|
28
28
|
| Edit | `memory_update(id, content)` | Correct or expand existing |
|
|
29
29
|
| Bulk save | `memory_batch_store(memories[])` | Efficient for multiple items |
|
|
30
|
-
| Build prompt | `memory_context(query, token_budget)` | Token-aware context window |
|
|
30
|
+
| Build prompt | `memory_context(query, token_budget)` | Token-aware context window (see `entity-and-context` skill) |
|
|
31
31
|
| Upgrade | `memory_promote(id, importance, tier)` | Move temporary to long-term |
|
|
32
32
|
|
|
33
|
+
**For architectural choices**, use `decision_record` instead of `memory_store` — see the `decision-tracking` skill.
|
|
34
|
+
|
|
35
|
+
**For reusable conventions**, use `pattern_create` instead of `memory_store` — see the `pattern-management` skill.
|
|
36
|
+
|
|
33
37
|
## Ending a Session
|
|
34
38
|
|
|
35
39
|
Call `session_end(session_id, summary)` with a meaningful summary. This becomes the historical record.
|
|
@@ -58,27 +62,13 @@ Categories: `technical`, `preference`, `credential`, `decision`. Consistent meta
|
|
|
58
62
|
|
|
59
63
|
Use `memory_promote(id, importance, tier)` to upgrade a memory. Set `importance` near 1.0 for critical items.
|
|
60
64
|
|
|
61
|
-
## Auto-Capture Tiers
|
|
62
|
-
|
|
63
|
-
Configured per-plugin, not per-call. Know what mode is active:
|
|
64
|
-
|
|
65
|
-
| Tier | Behavior |
|
|
66
|
-
|------|----------|
|
|
67
|
-
| `off` | No automatic capture |
|
|
68
|
-
| `conservative` | Only credentials and explicit preferences |
|
|
69
|
-
| `smart` (default) | Technical facts, preferences, patterns |
|
|
70
|
-
| `aggressive` | Captures most exchanged information |
|
|
71
|
-
|
|
72
|
-
## Privacy Blocklist
|
|
73
|
-
|
|
74
|
-
Auto-filtered (never stored): passwords, credit card numbers, SSNs, API keys/tokens. The blocklist regex rejects these even on manual `memory_store` calls.
|
|
75
|
-
|
|
76
65
|
## Common Mistakes
|
|
77
66
|
|
|
78
67
|
| Mistake | Fix |
|
|
79
68
|
|---------|-----|
|
|
80
69
|
| Storing without a session | Always call `session_start` first |
|
|
81
70
|
| Skipping `deduplicate=true` | Set it on every `memory_store` call |
|
|
82
|
-
| Using `memory_store` for decisions | Use `decision_record` instead |
|
|
71
|
+
| Using `memory_store` for decisions | Use `decision_record` instead (see `decision-tracking` skill) |
|
|
72
|
+
| Using `memory_store` for conventions | Use `pattern_create` instead (see `pattern-management` skill) |
|
|
83
73
|
| No category/tags in metadata | Always include both for searchability |
|
|
84
|
-
| Storing API keys or passwords | Blocklist rejects
|
|
74
|
+
| Storing API keys or passwords | Blocklist auto-rejects these; use a secrets manager |
|
|
@@ -5,22 +5,22 @@ description: "Use when establishing reusable conventions, looking up existing pa
|
|
|
5
5
|
|
|
6
6
|
# Pattern Management
|
|
7
7
|
|
|
8
|
-
Patterns are **reusable solutions with problem context**. Plain implementation notes are memories — use `memory_store` for those.
|
|
8
|
+
Patterns are **reusable solutions with problem context**. Plain implementation notes are memories — use `memory_store` for those (see `memory-workflow` skill).
|
|
9
9
|
|
|
10
10
|
## Pattern Tools
|
|
11
11
|
|
|
12
12
|
| Tool | Signature | Purpose |
|
|
13
13
|
|------|-----------|---------|
|
|
14
|
-
| `pattern_create` | `pattern_create(title, description)` | Create a reusable pattern |
|
|
15
|
-
| `pattern_search` | `pattern_search(query)` | Find existing patterns
|
|
14
|
+
| `pattern_create` | `pattern_create(title, description, category?, example_code?, scope?, tags?, source_project?)` | Create a reusable pattern |
|
|
15
|
+
| `pattern_search` | `pattern_search(query, category?, project?, limit?, threshold?)` | Find existing patterns |
|
|
16
16
|
| `pattern_adopt` | `pattern_adopt(pattern_id, project)` | Track which projects use which patterns |
|
|
17
|
-
| `pattern_suggest` | `pattern_suggest(project)` | Get pattern recommendations for a project |
|
|
17
|
+
| `pattern_suggest` | `pattern_suggest(project, limit?)` | Get pattern recommendations for a project |
|
|
18
18
|
|
|
19
19
|
## Workflow
|
|
20
20
|
|
|
21
21
|
1. **Search first** — Always call `pattern_search(query)` before `pattern_create`. Duplicate patterns fragment conventions and confuse future lookups.
|
|
22
|
-
2. **Create with structure** — If no match exists, call `pattern_create(title, description)`.
|
|
23
|
-
3. **Adopt to projects** — Call `pattern_adopt(pattern_id, project)` so the pattern appears in `project_context` results and `pattern_suggest` recommendations.
|
|
22
|
+
2. **Create with structure** — If no match exists, call `pattern_create(title, description, category, example_code)`. Include `example_code` for maximum usefulness. Set `scope` to `"global"` for cross-project or `"project"` for project-specific patterns.
|
|
23
|
+
3. **Adopt to projects** — Call `pattern_adopt(pattern_id, project)` so the pattern appears in `project_context` results and `pattern_suggest` recommendations (see `project-orchestration` skill).
|
|
24
24
|
4. **Review suggestions** — Use `pattern_suggest(project)` when starting work on a project to discover applicable conventions.
|
|
25
25
|
|
|
26
26
|
## Pattern Structure
|
|
@@ -35,10 +35,14 @@ Every `description` in `pattern_create` should include:
|
|
|
35
35
|
| **Examples** | Concrete usage showing the pattern in action |
|
|
36
36
|
|
|
37
37
|
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
pattern_create(
|
|
39
|
+
"RFC 7807 Error Responses",
|
|
40
|
+
"Problem: Inconsistent error responses across API endpoints.\nSolution: Return { error, code, details? } on all 4xx/5xx.\nContext: REST APIs with multiple consumers. Not needed for internal RPC.",
|
|
41
|
+
"error-handling",
|
|
42
|
+
'{ error: "Invalid email", code: "VALIDATION_ERROR", details: { field: "email" } }',
|
|
43
|
+
"global",
|
|
44
|
+
["api", "rest", "validation"]
|
|
45
|
+
)
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
## Cross-Project Consistency
|
|
@@ -53,5 +57,6 @@ Examples: 400 → { error: 'Invalid email', code: 'VALIDATION_ERROR' }"
|
|
|
53
57
|
|---------|-----|
|
|
54
58
|
| Creating without searching first | Always `pattern_search` before `pattern_create` — duplicates fragment conventions |
|
|
55
59
|
| Vague descriptions | Follow the Problem/Solution/Context/Examples structure; be specific and actionable |
|
|
60
|
+
| Omitting `example_code` | Always include a concrete code example — patterns without examples are hard to apply |
|
|
56
61
|
| Not adopting to projects | Call `pattern_adopt` after creating; unadopted patterns are invisible to `project_context` |
|
|
57
62
|
| Patterns too specific | Patterns should generalize across contexts; one-off solutions belong in memories or decisions |
|
|
@@ -5,22 +5,22 @@ description: "Use when registering a new project, loading project context before
|
|
|
5
5
|
|
|
6
6
|
# Project Orchestration
|
|
7
7
|
|
|
8
|
-
Projects are the top-level organizer. Every session, memory, decision, and pattern ties back to a project slug.
|
|
8
|
+
Projects are the top-level organizer. Every session, memory, decision, and pattern ties back to a project slug. See related skills: `memory-workflow`, `decision-tracking`, `pattern-management`.
|
|
9
9
|
|
|
10
10
|
## Project Tools
|
|
11
11
|
|
|
12
12
|
| Tool | Signature | Purpose |
|
|
13
13
|
|------|-----------|---------|
|
|
14
|
-
| `project_register` | `project_register(slug, name, description
|
|
15
|
-
| `project_list` | `project_list()` | List all registered projects |
|
|
14
|
+
| `project_register` | `project_register(slug, name, description?, stack?, repo_url?)` | Register a new project |
|
|
15
|
+
| `project_list` | `project_list(limit?)` | List all registered projects |
|
|
16
16
|
| `project_info` | `project_info(slug)` | Get project details |
|
|
17
|
+
| `project_context` | `project_context(slug)` | Full overview: hot-tier memories, active decisions, adopted patterns |
|
|
17
18
|
| `project_add_relationship` | `project_add_relationship(from, to, type)` | Link related projects |
|
|
18
19
|
| `project_dependencies` | `project_dependencies(slug)` | What this project depends on |
|
|
19
20
|
| `project_dependents` | `project_dependents(slug)` | What depends on this project |
|
|
20
21
|
| `project_related` | `project_related(slug)` | All related projects (any direction) |
|
|
21
|
-
| `project_impact` | `project_impact(
|
|
22
|
-
| `project_shared_patterns` | `project_shared_patterns(
|
|
23
|
-
| `project_context` | `project_context(slug)` | Full overview: hot-tier memories, active decisions, adopted patterns |
|
|
22
|
+
| `project_impact` | `project_impact(project, change_description)` | Impact analysis before breaking changes |
|
|
23
|
+
| `project_shared_patterns` | `project_shared_patterns(project_a, project_b)` | Patterns shared between two projects |
|
|
24
24
|
|
|
25
25
|
## First-Time Setup
|
|
26
26
|
|
|
@@ -37,14 +37,14 @@ When `defaultProject` is set in plugin configuration, the project slug is **auto
|
|
|
37
37
|
Always call `project_context(slug)` as the **first step** when beginning work on a project. It returns:
|
|
38
38
|
|
|
39
39
|
- **Hot-tier memories** — critical facts always surfaced
|
|
40
|
-
- **Active decisions** — current architectural choices
|
|
41
|
-
- **Adopted patterns** — conventions in use
|
|
40
|
+
- **Active decisions** — current architectural choices (see `decision-tracking` skill)
|
|
41
|
+
- **Adopted patterns** — conventions in use (see `pattern-management` skill)
|
|
42
42
|
|
|
43
43
|
This replaces manual searching. Start here, then drill into specifics with other tools.
|
|
44
44
|
|
|
45
45
|
## Impact Analysis
|
|
46
46
|
|
|
47
|
-
Before making **any breaking change**, call `project_impact(
|
|
47
|
+
Before making **any breaking change**, call `project_impact(project, change_description)` with a clear description of the proposed change. It checks:
|
|
48
48
|
|
|
49
49
|
- Downstream dependents that will be affected
|
|
50
50
|
- Shared patterns that may need updating
|
|
@@ -54,12 +54,12 @@ Never skip this step. Breaking a dependency without checking impact creates casc
|
|
|
54
54
|
|
|
55
55
|
## Managing Relationships
|
|
56
56
|
|
|
57
|
-
Use `project_add_relationship(from, to, type)` to declare how projects relate. Relationship types
|
|
57
|
+
Use `project_add_relationship(from, to, type)` to declare how projects relate. Relationship types: `depends_on`, `api_consumer`, `shares_schema`, `shares_infra`, `pattern_source`, `forked_from`. Once linked:
|
|
58
58
|
|
|
59
59
|
- `project_dependencies(slug)` — upstream projects this one relies on
|
|
60
60
|
- `project_dependents(slug)` — downstream projects relying on this one
|
|
61
61
|
- `project_related(slug)` — all connections regardless of direction
|
|
62
|
-
- `project_shared_patterns(
|
|
62
|
+
- `project_shared_patterns(project_a, project_b)` — patterns adopted by both projects
|
|
63
63
|
|
|
64
64
|
## Common Mistakes
|
|
65
65
|
|
|
@@ -68,5 +68,5 @@ Use `project_add_relationship(from, to, type)` to declare how projects relate. R
|
|
|
68
68
|
| Working without project context | Always `project_context` first — it loads everything you need |
|
|
69
69
|
| Registering duplicate projects | Call `project_list` before `project_register`; check if slug exists |
|
|
70
70
|
| Not using relationships | Call `project_add_relationship` when projects share code, APIs, or conventions |
|
|
71
|
-
| Breaking changes without impact check | Always `project_impact` before modifying shared interfaces
|
|
71
|
+
| Breaking changes without impact check | Always `project_impact(project, change_description)` before modifying shared interfaces |
|
|
72
72
|
| Passing project on every call when `defaultProject` is set | Unnecessary — the config injects it automatically |
|
package/src/status-reporter.ts
CHANGED
|
@@ -34,10 +34,25 @@ export interface MemoryStats {
|
|
|
34
34
|
search_count_24h?: number;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
export type AutoCaptureTier = "off" | "conservative" | "smart" | "aggressive";
|
|
38
|
+
|
|
39
|
+
export interface AutoCaptureConfig {
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
tier: AutoCaptureTier;
|
|
42
|
+
confirmFirst?: number;
|
|
43
|
+
categories?: {
|
|
44
|
+
credentials?: boolean;
|
|
45
|
+
preferences?: boolean;
|
|
46
|
+
technical?: boolean;
|
|
47
|
+
personal?: boolean;
|
|
48
|
+
};
|
|
49
|
+
blocklist?: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
export interface PluginConfig {
|
|
38
53
|
agentId: string;
|
|
39
54
|
autoRecall: boolean;
|
|
40
|
-
autoCapture:
|
|
55
|
+
autoCapture: AutoCaptureConfig;
|
|
41
56
|
recallLimit: number;
|
|
42
57
|
recallThreshold: number;
|
|
43
58
|
excludeChannels: string[];
|
|
@@ -189,7 +204,10 @@ export class StatusReporter {
|
|
|
189
204
|
? `✓ Enabled (limit: ${report.config.recallLimit}, threshold: ${report.config.recallThreshold})`
|
|
190
205
|
: "✗ Disabled";
|
|
191
206
|
lines.push(` Auto-Recall: ${recallStatus}`);
|
|
192
|
-
|
|
207
|
+
const captureStatus = report.config.autoCapture.enabled
|
|
208
|
+
? `✓ Enabled (tier: ${report.config.autoCapture.tier})`
|
|
209
|
+
: "✗ Disabled";
|
|
210
|
+
lines.push(` Auto-Capture: ${captureStatus}`);
|
|
193
211
|
if (report.config.defaultProject) {
|
|
194
212
|
lines.push(` Default Project: ${report.config.defaultProject}`);
|
|
195
213
|
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: codebase-navigation
|
|
3
|
-
description: "Use when navigating the openclaw-plugin codebase for the first time, looking for where a tool is registered, understanding the monolithic index.ts structure, or adding a new tool or hook."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Codebase Navigation
|
|
7
|
-
|
|
8
|
-
The plugin is a single monolithic `index.ts` (4839 lines) plus a few extracted modules.
|
|
9
|
-
|
|
10
|
-
## index.ts File Map
|
|
11
|
-
|
|
12
|
-
| Lines | Section |
|
|
13
|
-
|-------|---------|
|
|
14
|
-
| 1--14 | Header comments and version info |
|
|
15
|
-
| 16--36 | Imports (plugin SDK, heartbeat, CLI stats, onboarding) |
|
|
16
|
-
| 38--47 | Constants (`DEFAULT_API_URL`, `REQUEST_TIMEOUT_MS`, `MAX_RETRIES`) |
|
|
17
|
-
| 48--121 | `DebugLogger` class (inlined) with `LogEntry`, `DebugLoggerConfig` |
|
|
18
|
-
| 123--404 | `StatusReporter` class (inlined) with `ToolStatus`, `ConnectionStatus`, `MemoryStats` |
|
|
19
|
-
| 406--461 | Types: `AutoCaptureConfig`, `MemoryRelayConfig`, `Memory`, `SearchResult`, `Stats` |
|
|
20
|
-
| 463--648 | Utility functions: `sleep`, `isRetryableError`, `fetchWithTimeout`, auto-capture helpers, `redactSensitive`, `extractRescueContent` |
|
|
21
|
-
| 650--1294 | `MemoryRelayClient` class (API client with retry logic, all endpoints) |
|
|
22
|
-
| 1296--1315 | Pattern detection for auto-capture (`CAPTURE_PATTERNS`, `shouldCapture`) |
|
|
23
|
-
| 1317--1562 | Plugin entry: `export default async function plugin(api)`, config resolution, client init, session cache, `touchSession` |
|
|
24
|
-
| 1564--1613 | `TOOL_GROUPS` map and `isToolEnabled()` |
|
|
25
|
-
| 1615--3747 | 39 tool registrations |
|
|
26
|
-
| 3749--3853 | CLI commands (`memoryrelay status/stats/list/export`) |
|
|
27
|
-
| 3855--4240 | 14 lifecycle hooks (`before_agent_start`, `agent_end`, `session_start`, `session_end`, `before_tool_call`, `after_tool_call`, `before_compaction`, `before_reset`, `message_received`, `message_sending`, `before_message_write`, `subagent_spawned`, `subagent_ended`, `tool_result_persist`) |
|
|
28
|
-
| 4242--4274 | First-run onboarding |
|
|
29
|
-
| 4276--4586 | Gateway methods (`memoryrelay.logs`, `.health`, `.metrics`, `.heartbeat`, `.onboarding`, `.stats`, `.test`) |
|
|
30
|
-
| 4588--4790 | Direct commands: `/memory-status`, `/memory-stats`, `/memory-health`, `/memory-logs`, `/memory-metrics` |
|
|
31
|
-
| 4792--4839 | Stale session cleanup service (`memoryrelay-session-cleanup` via `api.registerService`) |
|
|
32
|
-
|
|
33
|
-
## Tool Registration Pattern
|
|
34
|
-
|
|
35
|
-
Every tool follows this pattern:
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
if (isToolEnabled("tool_name")) {
|
|
39
|
-
api.registerTool((ctx) => ({
|
|
40
|
-
name: "tool_name",
|
|
41
|
-
description: "...",
|
|
42
|
-
parameters: { /* JSON schema */ },
|
|
43
|
-
execute: async (_id, args) => { /* impl */ }
|
|
44
|
-
}), { name: "tool_name" });
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Tools are numbered 1--39 with comment markers (e.g., `// 1. memory_store`). Search for `// N.` to jump to a specific tool.
|
|
49
|
-
|
|
50
|
-
## TOOL_GROUPS (line 1569)
|
|
51
|
-
|
|
52
|
-
| Group | Count | Tools |
|
|
53
|
-
|-------|-------|-------|
|
|
54
|
-
| memory | 9 | `memory_store`, `memory_recall`, `memory_forget`, `memory_list`, `memory_get`, `memory_update`, `memory_batch_store`, `memory_context`, `memory_promote` |
|
|
55
|
-
| entity | 4 | `entity_create`, `entity_link`, `entity_list`, `entity_graph` |
|
|
56
|
-
| agent | 3 | `agent_list`, `agent_create`, `agent_get` |
|
|
57
|
-
| session | 4 | `session_start`, `session_end`, `session_recall`, `session_list` |
|
|
58
|
-
| decision | 4 | `decision_record`, `decision_list`, `decision_supersede`, `decision_check` |
|
|
59
|
-
| pattern | 4 | `pattern_create`, `pattern_search`, `pattern_adopt`, `pattern_suggest` |
|
|
60
|
-
| project | 10 | `project_register`, `project_list`, `project_info`, `project_add_relationship`, `project_dependencies`, `project_dependents`, `project_related`, `project_impact`, `project_shared_patterns`, `project_context` |
|
|
61
|
-
| health | 1 | `memory_health` |
|
|
62
|
-
|
|
63
|
-
The `enabledTools` config option accepts a comma-separated list of group names (or `"all"`).
|
|
64
|
-
|
|
65
|
-
## Supporting Modules
|
|
66
|
-
|
|
67
|
-
| File | Purpose |
|
|
68
|
-
|------|---------|
|
|
69
|
-
| `src/debug-logger.ts` | `DebugLogger` class (source of the inlined copy) |
|
|
70
|
-
| `src/status-reporter.ts` | `StatusReporter` class (source of the inlined copy) |
|
|
71
|
-
| `src/heartbeat/daily-stats.ts` | Morning/evening summaries, `calculateStats`, `formatStatsForDisplay` |
|
|
72
|
-
| `src/onboarding/first-run.ts` | Onboarding wizard: `checkFirstRun`, `runSimpleOnboarding` |
|
|
73
|
-
| `src/cli/stats-command.ts` | CLI `stats` command handler |
|
|
74
|
-
|
|
75
|
-
## Configuration Fallback Chain
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
Plugin config (openclaw.json) -> Env vars -> Defaults
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
- `apiKey`: `cfg.apiKey` -> `MEMORYRELAY_API_KEY` (required, no default)
|
|
82
|
-
- `agentId`: `cfg.agentId` -> `MEMORYRELAY_AGENT_ID` -> `api.agentName`
|
|
83
|
-
- `apiUrl`: `cfg.apiUrl` -> `MEMORYRELAY_API_URL` -> `https://api.memoryrelay.net`
|
|
84
|
-
|
|
85
|
-
## Key Types
|
|
86
|
-
|
|
87
|
-
| Type | Location | Purpose |
|
|
88
|
-
|------|----------|---------|
|
|
89
|
-
| `Memory` | L442 | Core memory record (`id`, `content`, `agent_id`, `metadata`, `entities`) |
|
|
90
|
-
| `SearchResult` | L453 | Vector search hit (`memory`, `score`) |
|
|
91
|
-
| `Stats` | L458 | Agent statistics (`total_memories`, `last_updated`) |
|
|
92
|
-
| `LogEntry` | L52 | Debug log entry (tool, method, path, duration, status) |
|
|
93
|
-
| `DebugLoggerConfig` | L66 | Logger settings (enabled, verbose, maxEntries) |
|
|
94
|
-
| `ConnectionStatus` | L140 | API connection state (status, endpoint, responseTime) |
|
|
95
|
-
| `ToolStatus` | L127 | Per-group tool health (enabled, available, failed) |
|
|
96
|
-
|
|
97
|
-
## Key Helpers
|
|
98
|
-
|
|
99
|
-
| Function | Location | Purpose |
|
|
100
|
-
|----------|----------|---------|
|
|
101
|
-
| `redactSensitive` | L591 | Replace blocklist patterns with `[REDACTED]` |
|
|
102
|
-
| `extractRescueContent` | L608 | Salvage assistant messages before compaction/reset |
|
|
103
|
-
| `touchSession` | L1444 | Update `lastActivityAt` timestamp in session cache |
|
|
104
|
-
| `isToolEnabled` | L1605 | Check if a tool's group is in the enabled set |
|
|
105
|
-
| `shouldCapture` | L1310 | Test text against `CAPTURE_PATTERNS` for auto-capture |
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: release-process
|
|
3
|
-
description: "Use when bumping the plugin version, preparing a release, updating the CHANGELOG, creating a pre-release audit branch, or triggering the NPM publish workflow."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Release Process
|
|
7
|
-
|
|
8
|
-
## Semantic Versioning (0.x series)
|
|
9
|
-
|
|
10
|
-
| Bump | When | Example |
|
|
11
|
-
|------|------|---------|
|
|
12
|
-
| **Patch** (0.x.Y) | Bug fixes, version string updates, doc fixes | 0.12.10 → 0.12.11 |
|
|
13
|
-
| **Minor** (0.X.0) | New tools, new features, new config options | 0.12.11 → 0.13.0 |
|
|
14
|
-
| **Major** (X.0.0) | Breaking API changes (not used yet) | 0.13.0 → 1.0.0 |
|
|
15
|
-
|
|
16
|
-
## Version Bump Locations
|
|
17
|
-
|
|
18
|
-
All three files must match the same version string:
|
|
19
|
-
|
|
20
|
-
| File | Field |
|
|
21
|
-
|------|-------|
|
|
22
|
-
| `package.json` | `"version": "X.Y.Z"` |
|
|
23
|
-
| `openclaw.plugin.json` | `"version": "X.Y.Z"` and description string |
|
|
24
|
-
| `index.ts` | Header comment `Version: X.Y.Z` |
|
|
25
|
-
|
|
26
|
-
## CHANGELOG.md Format
|
|
27
|
-
|
|
28
|
-
Follows [Keep a Changelog](https://keepachangelog.com/). Each release entry:
|
|
29
|
-
|
|
30
|
-
```markdown
|
|
31
|
-
## [X.Y.Z] - YYYY-MM-DD
|
|
32
|
-
### Added
|
|
33
|
-
- **Feature Name**: Description
|
|
34
|
-
### Changed
|
|
35
|
-
- Description
|
|
36
|
-
### Fixed
|
|
37
|
-
- **Bug Name**: Description
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Add a comparison link at the bottom of the file:
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
[X.Y.Z]: https://github.com/memoryrelay/openclaw-plugin/compare/vPREV...vX.Y.Z
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Git Commit Conventions
|
|
47
|
-
|
|
48
|
-
| Prefix | Use |
|
|
49
|
-
|--------|-----|
|
|
50
|
-
| `feat:` | New features or tools |
|
|
51
|
-
| `fix:` | Bug fixes |
|
|
52
|
-
| `docs:` | Documentation changes |
|
|
53
|
-
| `chore:` | Maintenance, deps, cleanup |
|
|
54
|
-
|
|
55
|
-
## CI/CD Workflows
|
|
56
|
-
|
|
57
|
-
| File | Trigger | Purpose |
|
|
58
|
-
|------|---------|---------|
|
|
59
|
-
| `.github/workflows/ci.yml` | Push/PR to main | Tests on Node 20.x + 22.x matrix |
|
|
60
|
-
| `.github/workflows/ci-cd.yml` | Push/PR | Full CI/CD pipeline |
|
|
61
|
-
| `.github/workflows/publish.yml` | Manual dispatch | NPM publish with version verification |
|
|
62
|
-
|
|
63
|
-
The publish workflow runs `npm publish --provenance --access public` and requires the `NPM_TOKEN` secret.
|
|
64
|
-
|
|
65
|
-
## Pre-Release Audit Branch
|
|
66
|
-
|
|
67
|
-
For doc review before release, create an audit branch:
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
docs/pre-release-audit-v{version}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Example: `docs/pre-release-audit-v0.12.11`. Merge to main once the audit is complete.
|
|
74
|
-
|
|
75
|
-
## Release Checklist
|
|
76
|
-
|
|
77
|
-
1. Update version in all 3 locations (`package.json`, `openclaw.plugin.json`, `index.ts`)
|
|
78
|
-
2. Update `CHANGELOG.md` with new entry and comparison link
|
|
79
|
-
3. Run `npm test` -- all tests must pass
|
|
80
|
-
4. Create audit branch (`docs/pre-release-audit-v{version}`) if doc changes are needed
|
|
81
|
-
5. Merge audit branch to main
|
|
82
|
-
6. Trigger the `publish.yml` workflow manually from GitHub Actions
|
|
83
|
-
7. Verify the published package on NPM
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: testing-memoryrelay
|
|
3
|
-
description: "Use when writing, running, or debugging tests for the MemoryRelay plugin, adding test coverage for new tools or hooks, or investigating test failures."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Testing MemoryRelay
|
|
7
|
-
|
|
8
|
-
Test runner: **Vitest**. All tests run without a live API.
|
|
9
|
-
|
|
10
|
-
## Commands
|
|
11
|
-
|
|
12
|
-
| Command | Purpose |
|
|
13
|
-
|---------|---------|
|
|
14
|
-
| `npm test` | Run all tests once (`vitest run`) |
|
|
15
|
-
| `npm run test:watch` | Watch mode (`vitest`) |
|
|
16
|
-
| `npm run test:coverage` | Coverage report (`vitest run --coverage`) |
|
|
17
|
-
|
|
18
|
-
## Test Files
|
|
19
|
-
|
|
20
|
-
| File | Scope |
|
|
21
|
-
|------|-------|
|
|
22
|
-
| `index.test.ts` | Integration tests: API client, tools, hooks, retry logic, pattern detection, channel filtering, tool groups, workflow instructions |
|
|
23
|
-
| `src/debug-logger.test.ts` | DebugLogger unit tests: circular buffer, filtering by tool/status, stats, formatting |
|
|
24
|
-
| `src/status-reporter.test.ts` | StatusReporter unit tests: failure tracking, report building, formatting |
|
|
25
|
-
|
|
26
|
-
## Mock Pattern
|
|
27
|
-
|
|
28
|
-
Tests use `MockMemoryRelayClient` -- an in-memory implementation that replaces the real API client:
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import { describe, test, expect, beforeEach, vi } from "vitest";
|
|
32
|
-
|
|
33
|
-
class MockMemoryRelayClient {
|
|
34
|
-
private memories: Memory[] = [];
|
|
35
|
-
private nextId = 1;
|
|
36
|
-
async store(content, metadata?) { /* push to array, return Memory */ }
|
|
37
|
-
async search(query, limit?, threshold?) { /* keyword .includes() match */ }
|
|
38
|
-
async list(limit?, offset?) { /* .slice() */ }
|
|
39
|
-
async get(id) { /* .find(), throws if missing */ }
|
|
40
|
-
async delete(id) { /* .splice(), throws if missing */ }
|
|
41
|
-
async health() { return { status: "healthy" }; }
|
|
42
|
-
async stats() { return { total_memories: this.memories.length }; }
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Instantiate fresh per test with `beforeEach(() => { client = new MockMemoryRelayClient("test_key", "test_agent"); })`.
|
|
47
|
-
|
|
48
|
-
## What to Test per Tool
|
|
49
|
-
|
|
50
|
-
| Area | Checks |
|
|
51
|
-
|------|--------|
|
|
52
|
-
| Input validation | Required params present, types correct |
|
|
53
|
-
| Success path | API response formatted correctly, data stored/returned |
|
|
54
|
-
| Error handling | Non-existent IDs throw, empty results return `[]` |
|
|
55
|
-
| Deduplication | `deduplicate=true` prevents near-duplicate storage |
|
|
56
|
-
| Session injection | `session_id` auto-applied from active session |
|
|
57
|
-
| Retry logic | Network errors and 5xx retried; 4xx not retried |
|
|
58
|
-
| Timeouts | 30s timeout via `AbortController` |
|
|
59
|
-
|
|
60
|
-
## Testing Hooks
|
|
61
|
-
|
|
62
|
-
**`before_agent_start`** -- workflow injection and auto-recall:
|
|
63
|
-
|
|
64
|
-
- Verify workflow instructions are built from enabled tool groups
|
|
65
|
-
- Mock `client.search()` to test auto-recall injects context
|
|
66
|
-
- Test channel exclusion skips auto-recall for blocklisted channels
|
|
67
|
-
|
|
68
|
-
**`agent_end`** -- auto-capture:
|
|
69
|
-
|
|
70
|
-
- Test pattern detection (`shouldCapture`) with regex matching
|
|
71
|
-
- Verify length bounds (20-2000 chars)
|
|
72
|
-
- Confirm privacy blocklist rejects passwords, SSNs, API keys
|
|
73
|
-
- Test tier logic: `off`, `conservative`, `smart`, `aggressive`
|
|
74
|
-
|
|
75
|
-
## Testing Gateway Methods
|
|
76
|
-
|
|
77
|
-
| Check | How |
|
|
78
|
-
|-------|-----|
|
|
79
|
-
| Response format | Assert returned object shape matches API contract |
|
|
80
|
-
| Error surfaces | `detail` field extracted (FastAPI format), falls back to `message` |
|
|
81
|
-
| HTTP method | GET with query params for search/check; POST with body for create/link |
|
|
82
|
-
|
|
83
|
-
## Unit Test Patterns
|
|
84
|
-
|
|
85
|
-
**DebugLogger**: Uses `vi.mock("fs")`. Test circular buffer (`maxEntries`), `getRecentLogs(n)`, `getToolLogs(name)`, `getErrorLogs()`, `getStats()`, `clear()`, `formatEntry()`.
|
|
86
|
-
|
|
87
|
-
**StatusReporter**: Instantiate with real `DebugLogger`. Test `recordFailure`/`recordSuccess` toggle, `buildReport` shape, `formatReport`/`formatCompact` output, disconnected status handling.
|