@memoryrelay/plugin-memoryrelay-ai 0.14.0 → 0.15.1

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
@@ -21,9 +21,10 @@ MemoryRelay is designed for engineering teams managing complex, long-running pro
21
21
  | Entities / knowledge graph | Yes (create, link, graph) | Yes | No |
22
22
  | Multi-agent collaboration | Yes (agent scoping, subagent tracking) | Limited | No |
23
23
  | Auto-capture with privacy tiers | Yes (off/conservative/smart/aggressive) | Basic | No |
24
- | Direct commands | 15 | ~5 | 0 |
24
+ | V2 Async Storage | Yes | No | No |
25
+ | Direct commands | 17 | ~5 | 0 |
25
26
  | Lifecycle hooks | 13 | 0 | 0 |
26
- | Tools | 39 | ~10 | 0 |
27
+ | Tools | 42 | ~10 | 0 |
27
28
 
28
29
  ## Quick Start
29
30
 
@@ -75,7 +76,7 @@ Auto-recall and smart auto-capture are enabled by default. The plugin injects re
75
76
  - Pattern adoption ensures consistent code style across sessions
76
77
  - Session tracking provides continuity when context windows reset
77
78
 
78
- ## Features -- 39 Tools by Category
79
+ ## Features -- 42 Tools by Category
79
80
 
80
81
  ### Memory (9 tools) -- group: `memory`
81
82
 
@@ -150,6 +151,14 @@ Auto-recall and smart auto-capture are enabled by default. The plugin injects re
150
151
  | `project_shared_patterns` | Find patterns shared between two projects |
151
152
  | `project_context` | Load full project context (memories, decisions, patterns, sessions) |
152
153
 
154
+ ### V2 Async (3 tools) -- group: `v2`
155
+
156
+ | Tool | Description |
157
+ |------|-------------|
158
+ | `memory_store_async` | Store a memory asynchronously and return a job ID |
159
+ | `memory_status` | Check the processing status of an async memory job |
160
+ | `context_build` | Build a ranked context bundle from relevant memories |
161
+
153
162
  ### Health (1 tool) -- group: `health`
154
163
 
155
164
  | Tool | Description |
@@ -165,6 +174,7 @@ These slash commands bypass the LLM and execute immediately.
165
174
  | Command | Description |
166
175
  |---------|-------------|
167
176
  | `/memory-search <query>` | Semantic search across stored memories |
177
+ | `/memory-context` | Build ranked context bundle from memories |
168
178
  | `/memory-sessions` | List sessions (optional: `active`, `closed`, or project slug) |
169
179
  | `/memory-decisions` | List architectural decisions (optional: project slug) |
170
180
  | `/memory-patterns` | List or search patterns (optional: search query) |
@@ -304,11 +314,29 @@ The plugin registers 14 lifecycle hooks:
304
314
 
305
315
  ### Skills
306
316
 
307
- The plugin ships with 8 skills providing guided workflows on top of the raw tools:
317
+ The plugin ships with 9 skills providing guided workflows on top of the raw tools:
308
318
 
309
319
  - **Agent-facing**: `memory-workflow`, `decision-tracking`, `pattern-management`, `project-orchestration`, `entity-and-context`
310
320
  - **Developer-facing**: `codebase-navigation`, `testing-memoryrelay`, `release-process`
311
321
 
322
+ ## Updating
323
+
324
+ To update to the latest version:
325
+
326
+ ```bash
327
+ openclaw plugins update plugin-memoryrelay-ai
328
+ ```
329
+
330
+ Or from within a conversation, run `/memory-update` to see the exact command.
331
+
332
+ **Important:** The plugin ID is `plugin-memoryrelay-ai` (not `memory-memoryrelay`). Using the wrong ID will fail with "No install record."
333
+
334
+ After updating, restart the gateway:
335
+
336
+ ```bash
337
+ openclaw restart
338
+ ```
339
+
312
340
  ## Troubleshooting
313
341
 
314
342
  ### Connection refused / API key issues
package/index.ts CHANGED
@@ -709,7 +709,7 @@ class MemoryRelayClient {
709
709
  headers: {
710
710
  "Content-Type": "application/json",
711
711
  Authorization: `Bearer ${this.apiKey}`,
712
- "User-Agent": "openclaw-memory-memoryrelay/0.13.0",
712
+ "User-Agent": "openclaw-memory-memoryrelay/0.15.1",
713
713
  },
714
714
  body: body ? JSON.stringify(body) : undefined,
715
715
  },
@@ -936,6 +936,63 @@ class MemoryRelayClient {
936
936
  });
937
937
  }
938
938
 
939
+ // --------------------------------------------------------------------------
940
+ // V2 Async API Methods (v0.15.0)
941
+ // --------------------------------------------------------------------------
942
+
943
+ async storeAsync(
944
+ content: string,
945
+ metadata?: Record<string, string>,
946
+ project?: string,
947
+ importance?: number,
948
+ tier?: string,
949
+ ): Promise<{ id: string; status: string; job_id: string; estimated_completion_seconds: number }> {
950
+ if (!content || content.length === 0 || content.length > 50000) {
951
+ throw new Error("Content must be between 1 and 50,000 characters");
952
+ }
953
+ const body: Record<string, unknown> = {
954
+ content,
955
+ agent_id: this.agentId,
956
+ };
957
+ if (metadata) body.metadata = metadata;
958
+ if (project) body.project = project;
959
+ if (importance != null) body.importance = importance;
960
+ if (tier) body.tier = tier;
961
+ return this.request("POST", "/v2/memories", body);
962
+ }
963
+
964
+ async getMemoryStatus(memoryId: string): Promise<{
965
+ id: string;
966
+ status: "pending" | "processing" | "ready" | "failed";
967
+ created_at: string;
968
+ updated_at: string;
969
+ error?: string;
970
+ }> {
971
+ return this.request("GET", `/v2/memories/${memoryId}/status`);
972
+ }
973
+
974
+ async buildContextV2(
975
+ query: string,
976
+ options?: {
977
+ maxMemories?: number;
978
+ maxTokens?: number;
979
+ aiEnhanced?: boolean;
980
+ searchMode?: "semantic" | "hybrid" | "keyword";
981
+ excludeMemoryIds?: string[];
982
+ },
983
+ ): Promise<any> {
984
+ const body: Record<string, unknown> = {
985
+ query,
986
+ agent_id: this.agentId,
987
+ };
988
+ if (options?.maxMemories != null) body.max_memories = options.maxMemories;
989
+ if (options?.maxTokens != null) body.max_tokens = options.maxTokens;
990
+ if (options?.aiEnhanced != null) body.ai_enhanced = options.aiEnhanced;
991
+ if (options?.searchMode) body.search_mode = options.searchMode;
992
+ if (options?.excludeMemoryIds) body.exclude_memory_ids = options.excludeMemoryIds;
993
+ return this.request("POST", "/v2/context", body);
994
+ }
995
+
939
996
  // --------------------------------------------------------------------------
940
997
  // Entity operations
941
998
  // --------------------------------------------------------------------------
@@ -1583,6 +1640,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
1583
1640
  "project_related", "project_impact", "project_shared_patterns", "project_context",
1584
1641
  ],
1585
1642
  health: ["memory_health"],
1643
+ v2: ["memory_store_async", "memory_status", "context_build"],
1586
1644
  };
1587
1645
 
1588
1646
  // Build a set of enabled tool names from group names
@@ -3746,6 +3804,202 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
3746
3804
  );
3747
3805
  }
3748
3806
 
3807
+ // 40. memory_store_async
3808
+ // --------------------------------------------------------------------------
3809
+ if (isToolEnabled("memory_store_async")) {
3810
+ api.registerTool((_ctx) => ({
3811
+ name: "memory_store_async",
3812
+ description:
3813
+ "Store a memory asynchronously using V2 API. Returns immediately (<50ms) with a job ID. Background workers generate the embedding. Use memory_status to poll for completion. Prefer this over memory_store for high-throughput or latency-sensitive applications." +
3814
+ (defaultProject ? ` Project defaults to '${defaultProject}' if not specified.` : ""),
3815
+ parameters: {
3816
+ type: "object",
3817
+ properties: {
3818
+ content: {
3819
+ type: "string",
3820
+ description: "The memory content to store (1–50,000 characters).",
3821
+ },
3822
+ metadata: {
3823
+ type: "object",
3824
+ description: "Optional key-value metadata to attach to the memory.",
3825
+ additionalProperties: { type: "string" },
3826
+ },
3827
+ project: {
3828
+ type: "string",
3829
+ description: "Project slug to associate with this memory (max 100 characters).",
3830
+ maxLength: 100,
3831
+ },
3832
+ importance: {
3833
+ type: "number",
3834
+ description: "Importance score (0-1). Higher values are retained longer.",
3835
+ minimum: 0,
3836
+ maximum: 1,
3837
+ },
3838
+ tier: {
3839
+ type: "string",
3840
+ description: "Memory tier: hot, warm, or cold.",
3841
+ enum: ["hot", "warm", "cold"],
3842
+ },
3843
+ },
3844
+ required: ["content"],
3845
+ },
3846
+ execute: async (
3847
+ _id,
3848
+ args: {
3849
+ content: string;
3850
+ metadata?: Record<string, string>;
3851
+ project?: string;
3852
+ importance?: number;
3853
+ tier?: string;
3854
+ },
3855
+ ) => {
3856
+ try {
3857
+ const { content, metadata, importance, tier } = args;
3858
+ let project = args.project;
3859
+ if (!project && defaultProject) project = defaultProject;
3860
+ const result = await client.storeAsync(content, metadata, project, importance, tier);
3861
+ return {
3862
+ content: [
3863
+ {
3864
+ type: "text",
3865
+ text: `Memory queued for async storage (id: ${result.id}, job_id: ${result.job_id}). Use memory_status to check completion.`,
3866
+ },
3867
+ ],
3868
+ details: result,
3869
+ };
3870
+ } catch (err) {
3871
+ return {
3872
+ content: [{ type: "text", text: `Failed to queue memory: ${String(err)}` }],
3873
+ details: { error: String(err) },
3874
+ };
3875
+ }
3876
+ },
3877
+ }), { name: "memory_store_async" });
3878
+ }
3879
+
3880
+ // 41. memory_status
3881
+ // --------------------------------------------------------------------------
3882
+ if (isToolEnabled("memory_status")) {
3883
+ api.registerTool((_ctx) => ({
3884
+ name: "memory_status",
3885
+ description:
3886
+ "Check the processing status of a memory created via memory_store_async. Status values: pending (waiting for worker), processing (generating embedding), ready (searchable), failed (error occurred).",
3887
+ parameters: {
3888
+ type: "object",
3889
+ properties: {
3890
+ memory_id: {
3891
+ type: "string",
3892
+ description: "The memory ID returned by memory_store_async.",
3893
+ },
3894
+ },
3895
+ required: ["memory_id"],
3896
+ },
3897
+ execute: async (
3898
+ _id,
3899
+ args: { memory_id: string },
3900
+ ) => {
3901
+ try {
3902
+ const status = await client.getMemoryStatus(args.memory_id);
3903
+ return {
3904
+ content: [
3905
+ {
3906
+ type: "text",
3907
+ text: JSON.stringify(status, null, 2),
3908
+ },
3909
+ ],
3910
+ details: status,
3911
+ };
3912
+ } catch (err) {
3913
+ return {
3914
+ content: [{ type: "text", text: `Failed to get memory status: ${String(err)}` }],
3915
+ details: { error: String(err) },
3916
+ };
3917
+ }
3918
+ },
3919
+ }), { name: "memory_status" });
3920
+ }
3921
+
3922
+ // 42. context_build
3923
+ // --------------------------------------------------------------------------
3924
+ if (isToolEnabled("context_build")) {
3925
+ api.registerTool((_ctx) => ({
3926
+ name: "context_build",
3927
+ description:
3928
+ "Build a ranked context bundle from memories with optional AI summarization. Searches for relevant memories, ranks them by composite score, and optionally generates an AI summary. Useful for building token-efficient context windows.",
3929
+ parameters: {
3930
+ type: "object",
3931
+ properties: {
3932
+ query: {
3933
+ type: "string",
3934
+ description: "The query to build context for.",
3935
+ },
3936
+ max_memories: {
3937
+ type: "number",
3938
+ description: "Maximum number of memories to include (1-100).",
3939
+ minimum: 1,
3940
+ maximum: 100,
3941
+ },
3942
+ max_tokens: {
3943
+ type: "number",
3944
+ description: "Maximum tokens for the context bundle (100-128000).",
3945
+ minimum: 100,
3946
+ maximum: 128000,
3947
+ },
3948
+ ai_enhanced: {
3949
+ type: "boolean",
3950
+ description: "If true, generate an AI summary of the retrieved memories.",
3951
+ },
3952
+ search_mode: {
3953
+ type: "string",
3954
+ description: "Search strategy: semantic, hybrid, or keyword.",
3955
+ enum: ["semantic", "hybrid", "keyword"],
3956
+ },
3957
+ exclude_memory_ids: {
3958
+ type: "array",
3959
+ description: "Memory IDs to exclude from results.",
3960
+ items: { type: "string" },
3961
+ },
3962
+ },
3963
+ required: ["query"],
3964
+ },
3965
+ execute: async (
3966
+ _id,
3967
+ args: {
3968
+ query: string;
3969
+ max_memories?: number;
3970
+ max_tokens?: number;
3971
+ ai_enhanced?: boolean;
3972
+ search_mode?: "semantic" | "hybrid" | "keyword";
3973
+ exclude_memory_ids?: string[];
3974
+ },
3975
+ ) => {
3976
+ try {
3977
+ const context = await client.buildContextV2(args.query, {
3978
+ maxMemories: args.max_memories,
3979
+ maxTokens: args.max_tokens,
3980
+ aiEnhanced: args.ai_enhanced,
3981
+ searchMode: args.search_mode,
3982
+ excludeMemoryIds: args.exclude_memory_ids,
3983
+ });
3984
+ return {
3985
+ content: [
3986
+ {
3987
+ type: "text",
3988
+ text: JSON.stringify(context, null, 2),
3989
+ },
3990
+ ],
3991
+ details: context,
3992
+ };
3993
+ } catch (err) {
3994
+ return {
3995
+ content: [{ type: "text", text: `Failed to build context: ${String(err)}` }],
3996
+ details: { error: String(err) },
3997
+ };
3998
+ }
3999
+ },
4000
+ }), { name: "context_build" });
4001
+ }
4002
+
3749
4003
  // ========================================================================
3750
4004
  // CLI Commands
3751
4005
  // ========================================================================
@@ -4236,7 +4490,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
4236
4490
  });
4237
4491
 
4238
4492
  api.logger.info?.(
4239
- `memory-memoryrelay: plugin v0.13.0 loaded (39 tools, autoRecall: ${cfg?.autoRecall}, autoCapture: ${autoCaptureConfig.enabled ? autoCaptureConfig.tier : 'off'}, debug: ${debugEnabled})`,
4493
+ `memory-memoryrelay: plugin v0.15.1 loaded (${Object.values(TOOL_GROUPS).flat().length} tools, autoRecall: ${cfg?.autoRecall}, autoCapture: ${autoCaptureConfig.enabled ? autoCaptureConfig.tier : 'off'}, debug: ${debugEnabled})`,
4240
4494
  );
4241
4495
 
4242
4496
  // ========================================================================
@@ -4644,7 +4898,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
4644
4898
  }
4645
4899
 
4646
4900
  // ========================================================================
4647
- // Direct Commands (15 total) — bypass LLM, execute immediately
4901
+ // Direct Commands (17 total) — bypass LLM, execute immediately
4648
4902
  // ========================================================================
4649
4903
 
4650
4904
  // /memory-status — Show full plugin status report
@@ -5279,6 +5533,80 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
5279
5533
  },
5280
5534
  });
5281
5535
 
5536
+ // /memory-context — Build a context bundle from memories using V2 API
5537
+ api.registerCommand?.({
5538
+ name: "memory-context",
5539
+ description: "Build a ranked context bundle from memories for a given query",
5540
+ requireAuth: true,
5541
+ acceptsArgs: true,
5542
+ handler: async (ctx) => {
5543
+ const { positional, flags } = parseCommandArgs(ctx.args);
5544
+ const query = positional.join(" ").trim();
5545
+ if (!query) {
5546
+ return {
5547
+ text: [
5548
+ "Usage: /memory-context <query> [options]",
5549
+ "",
5550
+ "Options:",
5551
+ " --max-memories <n> Maximum memories to include (1-100)",
5552
+ " --max-tokens <n> Maximum tokens for context (100-128000)",
5553
+ " --ai-enhanced Generate an AI summary of memories",
5554
+ " --search-mode <mode> Search strategy: semantic, hybrid, keyword",
5555
+ ].join("\n"),
5556
+ };
5557
+ }
5558
+ try {
5559
+ const options: {
5560
+ maxMemories?: number;
5561
+ maxTokens?: number;
5562
+ aiEnhanced?: boolean;
5563
+ searchMode?: "semantic" | "hybrid" | "keyword";
5564
+ } = {};
5565
+ if (flags["max-memories"]) options.maxMemories = parseInt(String(flags["max-memories"]), 10);
5566
+ if (flags["max-tokens"]) options.maxTokens = parseInt(String(flags["max-tokens"]), 10);
5567
+ if (flags["ai-enhanced"] === true) options.aiEnhanced = true;
5568
+ if (flags["search-mode"]) options.searchMode = String(flags["search-mode"]) as "semantic" | "hybrid" | "keyword";
5569
+
5570
+ const context = await client.buildContextV2(query, options);
5571
+
5572
+ if (!context || (Array.isArray(context.memories) && context.memories.length === 0)) {
5573
+ return { text: `No memories found for query: "${query}"` };
5574
+ }
5575
+
5576
+ return { text: JSON.stringify(context, null, 2) };
5577
+ } catch (err) {
5578
+ return { text: `Error: ${String(err)}`, isError: true };
5579
+ }
5580
+ },
5581
+ });
5582
+
5583
+ // /memory-update — Show update instructions (v0.15.0)
5584
+ api.registerCommand?.({
5585
+ name: "memory-update",
5586
+ description: "Show how to update the MemoryRelay plugin to the latest version",
5587
+ requireAuth: true,
5588
+ handler: async (_ctx) => {
5589
+ const currentVersion = "0.15.1";
5590
+ const lines: string[] = [
5591
+ "MemoryRelay Plugin Update",
5592
+ "━".repeat(50),
5593
+ `Current version: ${currentVersion}`,
5594
+ "",
5595
+ "To update to the latest version, run:",
5596
+ "",
5597
+ " openclaw plugins update plugin-memoryrelay-ai",
5598
+ "",
5599
+ "Then restart the gateway:",
5600
+ "",
5601
+ " openclaw restart",
5602
+ "",
5603
+ "Note: The plugin ID is 'plugin-memoryrelay-ai'",
5604
+ "(not 'memory-memoryrelay').",
5605
+ ];
5606
+ return { text: lines.join("\n") };
5607
+ },
5608
+ });
5609
+
5282
5610
  // ========================================================================
5283
5611
  // Stale Session Cleanup Service (v0.13.0)
5284
5612
  // ========================================================================
@@ -2,8 +2,8 @@
2
2
  "id": "plugin-memoryrelay-ai",
3
3
  "kind": "memory",
4
4
  "name": "MemoryRelay AI",
5
- "description": "MemoryRelay v0.14.0 - Long-term memory with 15 commands, sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
- "version": "0.14.0",
5
+ "description": "MemoryRelay v0.15.1 - Long-term memory with 42 tools, 17 commands, V2 async, sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
+ "version": "0.15.1",
7
7
  "uiHints": {
8
8
  "apiKey": {
9
9
  "label": "MemoryRelay API Key",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memoryrelay/plugin-memoryrelay-ai",
3
- "version": "0.14.0",
4
- "description": "OpenClaw memory plugin for MemoryRelay API - 15 commands, sessions, decisions, patterns, projects & semantic search",
3
+ "version": "0.15.1",
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",
7
7
  "scripts": {