@memoryrelay/plugin-memoryrelay-ai 0.12.10 → 0.13.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.
@@ -2,8 +2,8 @@
2
2
  "id": "plugin-memoryrelay-ai",
3
3
  "kind": "memory",
4
4
  "name": "MemoryRelay AI",
5
- "description": "MemoryRelay v0.12.6 - Long-term memory with sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
- "version": "0.12.10",
5
+ "description": "MemoryRelay v0.13.0 - Long-term memory with sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
+ "version": "0.13.0",
7
7
  "uiHints": {
8
8
  "apiKey": {
9
9
  "label": "MemoryRelay API Key",
@@ -45,6 +45,18 @@
45
45
  "label": "Exclude Channels",
46
46
  "help": "List of channel IDs to skip auto-recall (e.g., ['whatsapp:group_123', 'telegram:456'])",
47
47
  "advanced": true
48
+ },
49
+ "sessionTimeoutMinutes": {
50
+ "label": "Session Timeout (minutes)",
51
+ "placeholder": "120",
52
+ "advanced": true,
53
+ "help": "Auto-close MemoryRelay sessions inactive for this many minutes (default: 120)"
54
+ },
55
+ "sessionCleanupIntervalMinutes": {
56
+ "label": "Cleanup Interval (minutes)",
57
+ "placeholder": "30",
58
+ "advanced": true,
59
+ "help": "How often to check for stale sessions (default: 30)"
48
60
  }
49
61
  },
50
62
  "configSchema": {
@@ -101,6 +113,20 @@
101
113
  "default": [],
102
114
  "description": "List of channel IDs to exclude from auto-recall"
103
115
  },
116
+ "sessionTimeoutMinutes": {
117
+ "type": "number",
118
+ "default": 120,
119
+ "minimum": 10,
120
+ "maximum": 1440,
121
+ "description": "Auto-close sessions inactive for this many minutes"
122
+ },
123
+ "sessionCleanupIntervalMinutes": {
124
+ "type": "number",
125
+ "default": 30,
126
+ "minimum": 5,
127
+ "maximum": 360,
128
+ "description": "How often to check for stale sessions (minutes)"
129
+ },
104
130
  "debug": {
105
131
  "type": "boolean",
106
132
  "default": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoryrelay/plugin-memoryrelay-ai",
3
- "version": "0.12.10",
3
+ "version": "0.13.0",
4
4
  "description": "OpenClaw memory plugin for MemoryRelay API - sessions, decisions, patterns, projects & semantic search",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -48,7 +48,8 @@
48
48
  "openclaw.plugin.json",
49
49
  "README.md",
50
50
  "LICENSE",
51
- "src/"
51
+ "src/",
52
+ "skills/"
52
53
  ],
53
54
  "engines": {
54
55
  "node": ">=20.0.0"
@@ -0,0 +1,105 @@
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 |
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: decision-tracking
3
+ description: "Use when making architectural choices, evaluating alternatives, revisiting past decisions, or needing to check whether a decision already exists before committing to a direction."
4
+ ---
5
+
6
+ # Decision Tracking
7
+
8
+ Decisions are **choices with rationale and alternatives considered**. Plain facts, findings, or information are memories — use `memory_store` for those.
9
+
10
+ ## Decision Tools
11
+
12
+ | Tool | Signature | Purpose |
13
+ |------|-----------|---------|
14
+ | `decision_record` | `decision_record(title, rationale, project)` | Record a new decision with reasoning |
15
+ | `decision_list` | `decision_list(project)` | List all decisions for a project |
16
+ | `decision_supersede` | `decision_supersede(old_id, new_title, new_rationale)` | Replace an outdated decision |
17
+ | `decision_check` | `decision_check(query, project)` | Check for conflicting decisions before choosing |
18
+
19
+ ## Workflow
20
+
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)`. The rationale should include why this option was chosen and what alternatives were rejected.
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.
25
+
26
+ ## Project Scoping
27
+
28
+ Decisions must be scoped to the relevant project:
29
+
30
+ - Set `defaultProject` in plugin config to avoid passing `project` on every call.
31
+ - Pass `project` explicitly when working across multiple projects.
32
+ - Unscoped decisions pollute search results and create false conflicts.
33
+
34
+ ## Decisions vs Memories
35
+
36
+ | Store as Decision | Store as Memory |
37
+ |-------------------|-----------------|
38
+ | "Use PostgreSQL over MongoDB for user data" | "PostgreSQL supports JSONB columns" |
39
+ | "Adopt ESM modules, drop CommonJS" | "Node 20 supports ESM natively" |
40
+ | "API versioning via URL path, not headers" | "Team prefers REST over GraphQL" |
41
+
42
+ ## Common Mistakes
43
+
44
+ | Mistake | Fix |
45
+ |---------|-----|
46
+ | Recording decisions with `memory_store` | Use `decision_record` — decisions need rationale tracking and conflict detection |
47
+ | Making choices without `decision_check` | Always check first; conflicting decisions cause inconsistent architecture |
48
+ | Adding a new decision when one already exists | Use `decision_supersede` to replace the old decision, preserving history |
49
+ | Omitting the `project` parameter | Scope every decision to a project via parameter or `defaultProject` config |
50
+ | Storing facts as decisions | Only choices with rationale belong in decisions; use `memory_store` for information |
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: entity-and-context
3
+ description: "Use when building relationship maps between people, systems, services, or concepts that appear across multiple memories, or when recalling information that benefits from entity connections rather than flat search."
4
+ ---
5
+
6
+ # Entity and Context
7
+
8
+ Entities turn flat memory storage into a connected knowledge graph. Create entities for **named things that recur across multiple memories** and benefit from relationship tracking.
9
+
10
+ ## Relevant Tools
11
+
12
+ | Tool | Signature | Purpose |
13
+ |------|-----------|---------|
14
+ | `entity_create` | `entity_create(name, type, description)` | Create a new entity node |
15
+ | `entity_link` | `entity_link(entity_id, memory_id)` | Connect an entity to a memory |
16
+ | `entity_list` | `entity_list(type?)` | List entities, optionally filtered by type |
17
+ | `entity_graph` | `entity_graph(entity_id)` | Visualize an entity's relationships |
18
+ | `memory_context` | `memory_context(query)` | Enriched recall with entity connections (memory group tool) |
19
+
20
+ ## Entity Types
21
+
22
+ | Type | Examples |
23
+ |------|----------|
24
+ | `people` | Team members, stakeholders, external contacts |
25
+ | `systems` | Databases, cloud platforms, internal tools |
26
+ | `services` | APIs, microservices, third-party integrations |
27
+ | `concepts` | Architecture patterns, business domains, protocols |
28
+ | `teams` | Engineering squads, departments, working groups |
29
+ | `repositories` | Codebases, monorepos, package libraries |
30
+
31
+ ## When to Create Entities
32
+
33
+ Create an entity when a named thing:
34
+ - Appears in **2+ memories** (not one-off mentions)
35
+ - Has relationships worth tracking (owns, depends on, maintains)
36
+ - Would benefit from graph traversal during recall
37
+
38
+ ## Linking Workflow
39
+
40
+ 1. **Create the entity** — `entity_create("AuthService", "services", "Handles OAuth2 and JWT token management")`
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
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 all related memories and co-linked entities
45
+
46
+ ## memory_context vs memory_recall
47
+
48
+ | Use `memory_recall` | Use `memory_context` |
49
+ |---------------------|----------------------|
50
+ | Simple keyword/semantic search | Need entity relationships in results |
51
+ | No entities involved | Entities are linked to relevant memories |
52
+ | Quick lookup of isolated facts | Understanding how things connect |
53
+
54
+ ## Common Mistakes
55
+
56
+ | Mistake | Fix |
57
+ |---------|-----|
58
+ | Creating entities for one-off mentions | Only create entities for things referenced across multiple memories |
59
+ | Not linking entities to memories | An unlinked entity is invisible to `memory_context`; always link after creating |
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(type)` first to check if the entity already exists |
62
+ | Linking everything to one entity | Keep links specific; an entity should connect only to directly relevant memories |
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: memory-workflow
3
+ description: "Use when starting a new conversation or task that needs persistent memory, storing or retrieving information across sessions, or working within a project that uses MemoryRelay."
4
+ ---
5
+
6
+ # Memory Workflow
7
+
8
+ Follow this order every time. Skipping steps causes orphaned memories.
9
+
10
+ ## Startup Sequence
11
+
12
+ | Step | Call | Purpose |
13
+ |------|------|---------|
14
+ | 1 | `project_context(project)` | Load hot-tier memories, active decisions, adopted patterns |
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 |
18
+
19
+ ## During Work
20
+
21
+ | Action | Tool | Notes |
22
+ |--------|------|-------|
23
+ | Save info | `memory_store(content, metadata)` | Always set `deduplicate=true` |
24
+ | Search | `memory_recall(query)` | Semantic search across memories |
25
+ | Delete | `memory_forget(id_or_query)` | By ID or fuzzy search |
26
+ | Browse | `memory_list(limit, offset)` | Chronological listing |
27
+ | Read one | `memory_get(id)` | Fetch by exact ID |
28
+ | Edit | `memory_update(id, content)` | Correct or expand existing |
29
+ | Bulk save | `memory_batch_store(memories[])` | Efficient for multiple items |
30
+ | Build prompt | `memory_context(query, token_budget)` | Token-aware context window |
31
+ | Upgrade | `memory_promote(id, importance, tier)` | Move temporary to long-term |
32
+
33
+ ## Ending a Session
34
+
35
+ Call `session_end(session_id, summary)` with a meaningful summary. This becomes the historical record.
36
+
37
+ ## Deduplication
38
+
39
+ Always pass `deduplicate=true` on `memory_store` and `memory_batch_store`. The default threshold is 0.9 similarity. Skipping this clutters search results with near-duplicates.
40
+
41
+ ## Metadata Best Practices
42
+
43
+ Always include `category` and `tags` in metadata:
44
+
45
+ ```
46
+ metadata: { "category": "technical", "tags": "auth, api", "source": "code-review" }
47
+ ```
48
+
49
+ Categories: `technical`, `preference`, `credential`, `decision`. Consistent metadata makes filtering reliable.
50
+
51
+ ## Memory Tiers and Promotion
52
+
53
+ | Tier | Retention | Use for |
54
+ |------|-----------|---------|
55
+ | `hot` | Always loaded by `project_context` | Critical project facts |
56
+ | `warm` | Retrieved by search | General knowledge |
57
+ | `cold` | Archived, low priority | Historical notes |
58
+
59
+ Use `memory_promote(id, importance, tier)` to upgrade a memory. Set `importance` near 1.0 for critical items.
60
+
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
+ ## Common Mistakes
77
+
78
+ | Mistake | Fix |
79
+ |---------|-----|
80
+ | Storing without a session | Always call `session_start` first |
81
+ | Skipping `deduplicate=true` | Set it on every `memory_store` call |
82
+ | Using `memory_store` for decisions | Use `decision_record` instead |
83
+ | No category/tags in metadata | Always include both for searchability |
84
+ | Storing API keys or passwords | Blocklist rejects them; use a secrets manager |
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: pattern-management
3
+ description: "Use when establishing reusable conventions, looking up existing patterns before implementing a solution, adopting shared patterns across projects, or getting pattern recommendations for a project."
4
+ ---
5
+
6
+ # Pattern Management
7
+
8
+ Patterns are **reusable solutions with problem context**. Plain implementation notes are memories — use `memory_store` for those.
9
+
10
+ ## Pattern Tools
11
+
12
+ | Tool | Signature | Purpose |
13
+ |------|-----------|---------|
14
+ | `pattern_create` | `pattern_create(title, description)` | Create a reusable pattern |
15
+ | `pattern_search` | `pattern_search(query)` | Find existing patterns before creating new ones |
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 |
18
+
19
+ ## Workflow
20
+
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)`. The description must follow the structure below.
23
+ 3. **Adopt to projects** — Call `pattern_adopt(pattern_id, project)` so the pattern appears in `project_context` results and `pattern_suggest` recommendations.
24
+ 4. **Review suggestions** — Use `pattern_suggest(project)` when starting work on a project to discover applicable conventions.
25
+
26
+ ## Pattern Structure
27
+
28
+ Every `description` in `pattern_create` should include:
29
+
30
+ | Section | Content |
31
+ |---------|---------|
32
+ | **Problem** | What recurring issue this solves |
33
+ | **Solution** | The reusable approach or convention |
34
+ | **Context** | When to apply (and when not to) |
35
+ | **Examples** | Concrete usage showing the pattern in action |
36
+
37
+ ```
38
+ description: "Problem: Inconsistent error responses across API endpoints.
39
+ Solution: Return { error: string, code: string, details?: object } on all 4xx/5xx.
40
+ Context: REST APIs with multiple consumers. Not needed for internal RPC.
41
+ Examples: 400 → { error: 'Invalid email', code: 'VALIDATION_ERROR' }"
42
+ ```
43
+
44
+ ## Cross-Project Consistency
45
+
46
+ - `pattern_adopt(pattern_id, project)` links a pattern to a project. Multiple projects can adopt the same pattern.
47
+ - `pattern_suggest(project)` returns unadopted patterns that may be relevant based on project context.
48
+ - Adopted patterns load automatically via `project_context`, keeping teams aligned without manual lookups.
49
+
50
+ ## Common Mistakes
51
+
52
+ | Mistake | Fix |
53
+ |---------|-----|
54
+ | Creating without searching first | Always `pattern_search` before `pattern_create` — duplicates fragment conventions |
55
+ | Vague descriptions | Follow the Problem/Solution/Context/Examples structure; be specific and actionable |
56
+ | Not adopting to projects | Call `pattern_adopt` after creating; unadopted patterns are invisible to `project_context` |
57
+ | Patterns too specific | Patterns should generalize across contexts; one-off solutions belong in memories or decisions |
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: project-orchestration
3
+ description: "Use when registering a new project, loading project context before starting work, checking cross-project dependencies or impact before breaking changes, or managing relationships between projects."
4
+ ---
5
+
6
+ # Project Orchestration
7
+
8
+ Projects are the top-level organizer. Every session, memory, decision, and pattern ties back to a project slug.
9
+
10
+ ## Project Tools
11
+
12
+ | Tool | Signature | Purpose |
13
+ |------|-----------|---------|
14
+ | `project_register` | `project_register(slug, name, description, stack)` | Register a new project |
15
+ | `project_list` | `project_list()` | List all registered projects |
16
+ | `project_info` | `project_info(slug)` | Get project details |
17
+ | `project_add_relationship` | `project_add_relationship(from, to, type)` | Link related projects |
18
+ | `project_dependencies` | `project_dependencies(slug)` | What this project depends on |
19
+ | `project_dependents` | `project_dependents(slug)` | What depends on this project |
20
+ | `project_related` | `project_related(slug)` | All related projects (any direction) |
21
+ | `project_impact` | `project_impact(slug)` | Impact analysis before breaking changes |
22
+ | `project_shared_patterns` | `project_shared_patterns(slug)` | Patterns shared across related projects |
23
+ | `project_context` | `project_context(slug)` | Full overview: hot-tier memories, active decisions, adopted patterns |
24
+
25
+ ## First-Time Setup
26
+
27
+ 1. Call `project_list()` to see existing projects.
28
+ 2. If your project is not listed, call `project_register(slug, name, description, stack)`.
29
+ 3. If it exists, proceed — do not register duplicates.
30
+
31
+ ## defaultProject Config
32
+
33
+ When `defaultProject` is set in plugin configuration, the project slug is **auto-applied** to sessions, decisions, and memories. You do not need to pass the project parameter explicitly on each call — it is injected automatically.
34
+
35
+ ## Context Loading
36
+
37
+ Always call `project_context(slug)` as the **first step** when beginning work on a project. It returns:
38
+
39
+ - **Hot-tier memories** — critical facts always surfaced
40
+ - **Active decisions** — current architectural choices
41
+ - **Adopted patterns** — conventions in use
42
+
43
+ This replaces manual searching. Start here, then drill into specifics with other tools.
44
+
45
+ ## Impact Analysis
46
+
47
+ Before making **any breaking change**, call `project_impact(slug)`. It checks:
48
+
49
+ - Downstream dependents that will be affected
50
+ - Shared patterns that may need updating
51
+ - Active decisions that may conflict
52
+
53
+ Never skip this step. Breaking a dependency without checking impact creates cascading failures.
54
+
55
+ ## Managing Relationships
56
+
57
+ Use `project_add_relationship(from, to, type)` to declare how projects relate. Relationship types include dependencies, shared ownership, and related context. Once linked:
58
+
59
+ - `project_dependencies(slug)` — upstream projects this one relies on
60
+ - `project_dependents(slug)` — downstream projects relying on this one
61
+ - `project_related(slug)` — all connections regardless of direction
62
+ - `project_shared_patterns(slug)` — patterns adopted by both this project and its related projects
63
+
64
+ ## Common Mistakes
65
+
66
+ | Mistake | Fix |
67
+ |---------|-----|
68
+ | Working without project context | Always `project_context` first — it loads everything you need |
69
+ | Registering duplicate projects | Call `project_list` before `project_register`; check if slug exists |
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 or dependencies |
72
+ | Passing project on every call when `defaultProject` is set | Unnecessary — the config injects it automatically |
@@ -0,0 +1,83 @@
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
@@ -0,0 +1,87 @@
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.