@memorilabs/openclaw-memori 0.0.7 → 0.0.9

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
@@ -67,16 +67,15 @@ Memori enforces user-scoped memory boundaries for secure multi-user deployments.
67
67
 
68
68
  The Memori plugin replaces OpenClaw's flat-file memory workflow with managed, structured memory that is scoped by `entity_id`, `process_id`, and `session_id` and enriched automatically through OpenClaw's existing hooks.
69
69
 
70
- | Capability | What changes |
71
- | --- | --- |
72
- | **Structured memory storage** | Instead of raw markdown blobs, Memori stores conversations, facts, preferences, and knowledge-graph triples as structured records tied to an entity, process, and session. Facts are extracted as subject-predicate-object relationships, deduplicated over time, and connected into a graph so related memories stay queryable instead of being buried in text files. |
73
- | **Advanced Augmentation** | After each conversation, Memori processes the user and assistant exchange asynchronously in the background, identifies facts, preferences, skills, and attributes, generates embeddings for semantic search, and updates the knowledge graph without blocking the agent's response path. |
74
- | **Intelligent Recall** | Before the agent responds, Memori searches the current entity's stored facts and knowledge graph, ranks memories by semantic relevance and importance, and injects the most useful context into the prompt so durable knowledge survives context-window compression. |
75
- | **Production-ready observability** | Memori Cloud gives you dashboard visibility into memory creation, recalls, cache hit rate, sessions, quota usage, top subjects, per-memory retrieval metrics, and knowledge-graph relationships, so you can inspect what was stored and how recall is behaving in production. |
70
+ | Capability | What changes |
71
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
72
+ | **Structured memory storage** | Instead of raw markdown blobs, Memori stores conversations, facts, preferences, and knowledge-graph triples as structured records tied to an entity, process, and session. Facts are extracted as subject-predicate-object relationships, deduplicated over time, and connected into a graph so related memories stay queryable instead of being buried in text files. |
73
+ | **Advanced Augmentation** | After each conversation, Memori processes the user and assistant exchange asynchronously in the background, identifies facts, preferences, skills, and attributes, generates embeddings for semantic search, and updates the knowledge graph without blocking the agent's response path. |
74
+ | **Intelligent Recall** | Before the agent responds, Memori searches the current entity's stored facts and knowledge graph, ranks memories by semantic relevance and importance, and injects the most useful context into the prompt so durable knowledge survives context-window compression. |
75
+ | **Production-ready observability** | Memori Cloud gives you dashboard visibility into memory creation, recalls, cache hit rate, sessions, quota usage, top subjects, per-memory retrieval metrics, and knowledge-graph relationships, so you can inspect what was stored and how recall is behaving in production. |
76
76
 
77
77
  The plugin still remains drop-in: OpenClaw handles the agent loop, while Memori adds recall, augmentation, sanitization, and observability around it.
78
78
 
79
-
80
79
  ## Quickstart
81
80
 
82
81
  Get persistent memory running in your OpenClaw gateway in three steps.
@@ -86,36 +85,47 @@ Get persistent memory running in your OpenClaw gateway in three steps.
86
85
  - [OpenClaw](https://openclaw.ai) `v2026.3.2` or later
87
86
  - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai)
88
87
  - An Entity ID to attribute memories to, such as a user ID, tenant ID, or agent name
88
+ - A Project ID to scope memories to a specific project or workspace
89
89
 
90
90
  ### 1. Install and Enable
91
91
 
92
- Run the following commands in your terminal to install and enable the plugin:
93
-
94
92
  ```bash
95
- # 1. Install the plugin from npm
93
+ # Install the plugin from npm
96
94
  openclaw plugins install @memorilabs/openclaw-memori
97
95
 
98
- # 2. Enable it in your workspace
96
+ # Enable it in your workspace
99
97
  openclaw plugins enable openclaw-memori
100
-
101
- # 3. Restart the OpenClaw gateway
102
- openclaw gateway restart
103
98
  ```
104
99
 
105
100
  ### 2. Configure
106
101
 
107
- The plugin needs your Memori API key and an Entity ID to function. You can configure this via the OpenClaw CLI or your `openclaw.json` file.
102
+ The plugin requires an API key, an Entity ID, and a Project ID. Use the built-in `memori` CLI to configure it in one step.
108
103
 
109
- ### Option A: Via OpenClaw CLI (Recommended)
104
+ #### Option A: Memori CLI (Recommended)
110
105
 
111
106
  ```bash
112
- openclaw config set plugins.entries.openclaw-memori.config.apiKey "YOUR_MEMORI_API_KEY"
113
- openclaw config set plugins.entries.openclaw-memori.config.entityId "your-app-user-id"
107
+ openclaw memori init \
108
+ --api-key "YOUR_MEMORI_API_KEY" \
109
+ --entity-id "your-entity-id" \
110
+ --project-id "your-project-id"
114
111
  ```
115
112
 
116
- ### Option B: Via `openclaw.json`
113
+ Then restart the gateway:
114
+
115
+ ```bash
116
+ openclaw gateway restart
117
+ ```
117
118
 
118
- Add the following to your `~/.openclaw/openclaw.json` file:
119
+ #### Option B: OpenClaw config set
120
+
121
+ ```bash
122
+ openclaw config set plugins.entries.openclaw-memori.config.apiKey "YOUR_MEMORI_API_KEY"
123
+ openclaw config set plugins.entries.openclaw-memori.config.entityId "your-entity-id"
124
+ openclaw config set plugins.entries.openclaw-memori.config.projectId "your-project-id"
125
+ openclaw gateway restart
126
+ ```
127
+
128
+ #### Option C: Direct JSON (`~/.openclaw/openclaw.json`)
119
129
 
120
130
  ```json
121
131
  {
@@ -125,7 +135,8 @@ Add the following to your `~/.openclaw/openclaw.json` file:
125
135
  "enabled": true,
126
136
  "config": {
127
137
  "apiKey": "your-memori-api-key",
128
- "entityId": "your-app-user-id"
138
+ "entityId": "your-entity-id",
139
+ "projectId": "your-project-id"
129
140
  }
130
141
  }
131
142
  }
@@ -135,25 +146,42 @@ Add the following to your `~/.openclaw/openclaw.json` file:
135
146
 
136
147
  ### Configuration Options
137
148
 
138
- | Option | Type | Required | Description |
139
- | ---------- | -------- | -------- | --------------------------------------------------------------------------------------------------- |
140
- | `apiKey` | `string` | **Yes** | Your Memori API key. |
141
- | `entityId` | `string` | **Yes** | The unique identifier for the entity (e.g., user, agent, or tenant) to attribute these memories to. |
149
+ | Option | Type | Required | Description |
150
+ | ----------- | -------- | -------- | --------------------------------------------------------------------------------------------------- |
151
+ | `apiKey` | `string` | **Yes** | Your Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai). |
152
+ | `entityId` | `string` | **Yes** | The unique identifier for the entity (e.g., user, agent, or tenant) to attribute these memories to. |
153
+ | `projectId` | `string` | **Yes** | Scopes all memories to a specific project or workspace. |
142
154
 
143
155
  ### 3. Verify
144
156
 
145
- Restart the gateway and inspect the logs:
157
+ Check that the plugin is configured and can reach the API:
146
158
 
147
159
  ```bash
148
- openclaw gateway restart
149
- openclaw gateway logs --filter "[Memori]"
160
+ openclaw memori status --check
150
161
  ```
151
162
 
152
163
  You should see:
153
164
 
165
+ ```text
166
+ Memori Plugin Status
167
+ ────────────────────────────────────
168
+ API Key: ****...A3xQ
169
+ Entity ID: your-entity-id
170
+ Project ID: your-project-id
171
+
172
+ Checking API connectivity... OK
173
+ Status: Ready
174
+ ```
175
+
176
+ You can also inspect gateway logs to confirm the plugin loaded:
177
+
178
+ ```bash
179
+ openclaw gateway logs --filter "[Memori]"
180
+ ```
181
+
154
182
  ```text
155
183
  [Memori] === INITIALIZING PLUGIN ===
156
- [Memori] Tracking Entity ID: your-app-user-id
184
+ [Memori] Tracking Entity ID: your-entity-id
157
185
  ```
158
186
 
159
187
  To test the full memory loop:
@@ -167,6 +195,53 @@ To test the full memory loop:
167
195
  4. Confirm recall ran:
168
196
  `Successfully injected memory context.`
169
197
 
198
+ ---
199
+
200
+ ## CLI Reference
201
+
202
+ The plugin registers a `memori` command group in the OpenClaw CLI.
203
+
204
+ ### `openclaw memori init`
205
+
206
+ Configure the plugin with your credentials. All three flags are required.
207
+
208
+ ```bash
209
+ openclaw memori init \
210
+ --api-key <key> \
211
+ --entity-id <id> \
212
+ --project-id <id>
213
+ ```
214
+
215
+ ### `openclaw memori status`
216
+
217
+ Show the current configuration and whether the plugin is ready to run. Add `--check` to test live API connectivity.
218
+
219
+ ```bash
220
+ openclaw memori status
221
+ openclaw memori status --check
222
+ ```
223
+
224
+ ### `openclaw memori config`
225
+
226
+ Fine-grained configuration management.
227
+
228
+ ```bash
229
+ # Show all current values
230
+ openclaw memori config show
231
+
232
+ # Get a single value (API key is masked)
233
+ openclaw memori config get api-key
234
+ openclaw memori config get entity-id
235
+ openclaw memori config get project-id
236
+
237
+ # Set a single value
238
+ openclaw memori config set api-key "NEW_KEY"
239
+ openclaw memori config set entity-id "new-entity"
240
+ openclaw memori config set project-id "new-project"
241
+ ```
242
+
243
+ Valid keys: `api-key`, `entity-id`, `project-id`.
244
+
170
245
  ## How It Works
171
246
 
172
247
  This plugin integrates with OpenClaw's event lifecycle to provide persistent memory without interfering with the agent's core logic:
@@ -0,0 +1,2 @@
1
+ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
2
+ export declare function registerCliCommands(api: OpenClawPluginApi): void;
@@ -0,0 +1,143 @@
1
+ import { createRecallClient } from '../utils/memori-client.js';
2
+ import { CONFIG_KEY_MAP, readPluginConfig, writePluginConfig } from './config-file.js';
3
+ function maskKey(key) {
4
+ if (key.length <= 8)
5
+ return '****';
6
+ return `****...${key.slice(-4)}`;
7
+ }
8
+ function isReady(cfg) {
9
+ return Boolean(cfg.apiKey && cfg.entityId && cfg.projectId);
10
+ }
11
+ export function registerCliCommands(api) {
12
+ api.registerCli(({ program }) => {
13
+ const memori = program
14
+ .command('memori')
15
+ .description('Memori memory plugin commands')
16
+ .configureHelp({ sortSubcommands: true });
17
+ // ── init ────────────────────────────────────────────────────────────────
18
+ memori
19
+ .command('init')
20
+ .description('Configure the Memori plugin with your API credentials')
21
+ .option('--api-key <key>', 'Memori API key (from app.memorilabs.ai)')
22
+ .option('--entity-id <id>', 'Entity ID to scope all memories to')
23
+ .option('--project-id <id>', 'Project ID to scope all memories to')
24
+ .action((opts) => {
25
+ const missing = [];
26
+ if (!opts.apiKey)
27
+ missing.push('--api-key');
28
+ if (!opts.entityId)
29
+ missing.push('--entity-id');
30
+ if (!opts.projectId)
31
+ missing.push('--project-id');
32
+ if (missing.length > 0) {
33
+ console.error(`Error: missing required option(s): ${missing.join(', ')}`);
34
+ console.error('\nUsage:\n openclaw memori init --api-key <key> --entity-id <id> --project-id <id>');
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ writePluginConfig({
39
+ apiKey: opts.apiKey,
40
+ entityId: opts.entityId,
41
+ projectId: opts.projectId,
42
+ });
43
+ console.log('\nMemori configured successfully.\n');
44
+ console.log(` API Key: ${maskKey(opts.apiKey || '')}`);
45
+ console.log(` Entity ID: ${opts.entityId}`);
46
+ console.log(` Project ID: ${opts.projectId}`);
47
+ console.log('\nRestart the gateway to apply changes:');
48
+ console.log(' openclaw gateway restart\n');
49
+ });
50
+ // ── status ──────────────────────────────────────────────────────────────
51
+ memori
52
+ .command('status')
53
+ .description('Show current configuration and verify API connectivity')
54
+ .option('--check', 'Test live API connectivity')
55
+ .action(async (opts) => {
56
+ const cfg = readPluginConfig();
57
+ const ready = isReady(cfg);
58
+ console.log('\nMemori Plugin Status');
59
+ console.log('─'.repeat(36));
60
+ console.log(` API Key: ${cfg.apiKey ? maskKey(cfg.apiKey) : '(not set)'}`);
61
+ console.log(` Entity ID: ${cfg.entityId ?? '(not set)'}`);
62
+ console.log(` Project ID: ${cfg.projectId ?? '(not set)'}`);
63
+ console.log();
64
+ if (!ready) {
65
+ console.log('Status: Not configured.');
66
+ console.log('Run: openclaw memori init --api-key <key> --entity-id <id> --project-id <id>\n');
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ if (opts.check) {
71
+ process.stdout.write('Checking API connectivity... ');
72
+ try {
73
+ const client = createRecallClient(cfg.apiKey || '', cfg.entityId || '');
74
+ await client.agentRecall({ projectId: cfg.projectId });
75
+ console.log('OK');
76
+ }
77
+ catch (e) {
78
+ console.log('FAILED');
79
+ console.error(` ${String(e)}\n`);
80
+ process.exitCode = 1;
81
+ return;
82
+ }
83
+ }
84
+ console.log('Status: Ready\n');
85
+ });
86
+ // ── config ──────────────────────────────────────────────────────────────
87
+ const configCmd = memori
88
+ .command('config')
89
+ .description('Manage plugin configuration')
90
+ .configureHelp({ sortSubcommands: true });
91
+ const KEYS = Object.keys(CONFIG_KEY_MAP).join(', ');
92
+ configCmd
93
+ .command('show')
94
+ .description('Display current configuration')
95
+ .action(() => {
96
+ const cfg = readPluginConfig();
97
+ console.log('\nMemori Plugin Configuration');
98
+ console.log('─'.repeat(36));
99
+ console.log(` api-key: ${cfg.apiKey ? maskKey(cfg.apiKey) : '(not set)'}`);
100
+ console.log(` entity-id: ${cfg.entityId ?? '(not set)'}`);
101
+ console.log(` project-id: ${cfg.projectId ?? '(not set)'}`);
102
+ console.log();
103
+ });
104
+ configCmd
105
+ .command('get')
106
+ .argument('<key>', `Config key — one of: ${KEYS}`)
107
+ .description('Get a specific config value')
108
+ .action((key) => {
109
+ if (!(key in CONFIG_KEY_MAP)) {
110
+ console.error(`Unknown key "${key}". Valid keys: ${KEYS}`);
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ const field = CONFIG_KEY_MAP[key];
115
+ const value = readPluginConfig()[field];
116
+ if (value === undefined) {
117
+ console.log('(not set)');
118
+ }
119
+ else {
120
+ console.log(field === 'apiKey' ? maskKey(value) : value);
121
+ }
122
+ });
123
+ configCmd
124
+ .command('set')
125
+ .argument('<key>', `Config key — one of: ${KEYS}`)
126
+ .argument('<value>', 'Value to set')
127
+ .description('Set a specific config value')
128
+ .action((key, value) => {
129
+ if (!(key in CONFIG_KEY_MAP)) {
130
+ console.error(`Unknown key "${key}". Valid keys: ${KEYS}`);
131
+ process.exitCode = 1;
132
+ return;
133
+ }
134
+ const field = CONFIG_KEY_MAP[key];
135
+ writePluginConfig({ [field]: value });
136
+ console.log(`Set ${key}.`);
137
+ });
138
+ }, {
139
+ descriptors: [
140
+ { name: 'memori', description: 'Memori memory plugin commands', hasSubcommands: true },
141
+ ],
142
+ });
143
+ }
@@ -0,0 +1,8 @@
1
+ export interface MemoriCLIConfig {
2
+ apiKey?: string;
3
+ entityId?: string;
4
+ projectId?: string;
5
+ }
6
+ export declare function readPluginConfig(): MemoriCLIConfig;
7
+ export declare function writePluginConfig(updates: Partial<MemoriCLIConfig>): void;
8
+ export declare const CONFIG_KEY_MAP: Record<string, keyof MemoriCLIConfig>;
@@ -0,0 +1,64 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { dirname, join } from 'path';
4
+ const PLUGIN_ID = 'openclaw-memori';
5
+ const CONFIG_PATH = join(homedir(), '.openclaw', 'openclaw.json');
6
+ function readFullConfig() {
7
+ if (!existsSync(CONFIG_PATH))
8
+ return {};
9
+ try {
10
+ return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
11
+ }
12
+ catch (e) {
13
+ throw new Error(`Failed to parse OpenClaw config at ${CONFIG_PATH}: ${String(e)}`);
14
+ }
15
+ }
16
+ function writeFullConfig(config) {
17
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
18
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf-8');
19
+ }
20
+ function ensurePluginStructure(config) {
21
+ if (!config.plugins || typeof config.plugins !== 'object')
22
+ config.plugins = {};
23
+ const plugins = config.plugins;
24
+ if (!plugins.entries || typeof plugins.entries !== 'object')
25
+ plugins.entries = {};
26
+ const entries = plugins.entries;
27
+ if (!entries[PLUGIN_ID] || typeof entries[PLUGIN_ID] !== 'object')
28
+ entries[PLUGIN_ID] = {};
29
+ const entry = entries[PLUGIN_ID];
30
+ if (!entry.config || typeof entry.config !== 'object')
31
+ entry.config = {};
32
+ }
33
+ function getPluginConfigBlock(full) {
34
+ const plugins = full.plugins;
35
+ const entries = plugins?.entries;
36
+ const entry = entries?.[PLUGIN_ID];
37
+ return entry?.config;
38
+ }
39
+ export function readPluginConfig() {
40
+ const cfg = getPluginConfigBlock(readFullConfig());
41
+ if (!cfg)
42
+ return {};
43
+ return {
44
+ apiKey: cfg.apiKey,
45
+ entityId: cfg.entityId,
46
+ projectId: cfg.projectId,
47
+ };
48
+ }
49
+ export function writePluginConfig(updates) {
50
+ const full = readFullConfig();
51
+ ensurePluginStructure(full);
52
+ const entry = full.plugins.entries[PLUGIN_ID];
53
+ const cfg = entry.config;
54
+ for (const [key, value] of Object.entries(updates)) {
55
+ if (value)
56
+ cfg[key] = value;
57
+ }
58
+ writeFullConfig(full);
59
+ }
60
+ export const CONFIG_KEY_MAP = {
61
+ 'api-key': 'apiKey',
62
+ 'entity-id': 'entityId',
63
+ 'project-id': 'projectId',
64
+ };
@@ -1,5 +1,5 @@
1
1
  import { extractContext, initializeMemoriClient } from '../utils/index.js';
2
- import { cleanText, isSystemMessage } from '../sanitizer.js';
2
+ import { cleanText, extractContentType, isSystemMessage } from '../sanitizer.js';
3
3
  import { AUGMENTATION_CONFIG, MESSAGE_CONSTANTS, ROLE } from '../constants.js';
4
4
  import { SDK_VERSION } from '../version.js';
5
5
  /**
@@ -73,7 +73,9 @@ function extractToolCalls(msg, toolResults) {
73
73
  */
74
74
  function parseUserMessage(msg) {
75
75
  const cleanedContent = cleanText(msg.content);
76
- return cleanedContent ? { role: msg.role, content: cleanedContent } : null;
76
+ return cleanedContent
77
+ ? { role: msg.role, content: cleanedContent, type: extractContentType(msg.content) }
78
+ : null;
77
79
  }
78
80
  /**
79
81
  * Parses the most recent conversation turn from messages.
@@ -102,10 +104,15 @@ function parseTurnFromMessages(messages) {
102
104
  assistantMessage = {
103
105
  role: msg.role,
104
106
  content: cleanedContent.replace(/^\[\[.*?\]\]\s*/, ''),
107
+ type: extractContentType(msg.content),
105
108
  };
106
109
  }
107
110
  else if (extractedTools.length > 0) {
108
- assistantMessage = { role: msg.role, content: MESSAGE_CONSTANTS.SILENT_REPLY };
111
+ assistantMessage = {
112
+ role: msg.role,
113
+ content: MESSAGE_CONSTANTS.SILENT_REPLY,
114
+ type: extractContentType(msg.content),
115
+ };
109
116
  }
110
117
  }
111
118
  }
@@ -164,8 +171,8 @@ export async function handleAugmentation(event, ctx, config, logger) {
164
171
  logger.info('Assistant used tool-based messaging. Using synthetic response.');
165
172
  turn.assistantMessage.content = MESSAGE_CONSTANTS.SYNTHETIC_RESPONSE;
166
173
  }
167
- const payload = buildAugmentationPayload(turn.userMessage.content, turn.assistantMessage.content, turn.tools, event);
168
- const context = extractContext(event, ctx, config.entityId);
174
+ const payload = buildAugmentationPayload(turn.userMessage, turn.assistantMessage, turn.tools, event);
175
+ const context = extractContext(event, ctx, config.entityId, config.projectId);
169
176
  const memoriClient = initializeMemoriClient(config.apiKey, context);
170
177
  await memoriClient.augmentation(payload);
171
178
  logger.info('Augmentation successful!');
package/dist/index.js CHANGED
@@ -1,26 +1,33 @@
1
- import { handleRecall } from './handlers/recall.js';
2
1
  import { handleAugmentation } from './handlers/augmentation.js';
3
2
  import { PLUGIN_CONFIG } from './constants.js';
4
- import { MemoriLogger } from './utils/index.js';
3
+ import { MemoriLogger, loadSkillsContent } from './utils/index.js';
4
+ import { registerAllTools } from './tools/index.js';
5
+ import { registerCliCommands } from './cli/commands.js';
5
6
  const memoriPlugin = {
6
7
  id: PLUGIN_CONFIG.ID,
7
8
  name: PLUGIN_CONFIG.NAME,
8
9
  description: 'Hosted memory backend',
9
10
  register(api) {
11
+ registerCliCommands(api);
10
12
  const rawConfig = api.pluginConfig;
11
13
  const config = {
12
14
  apiKey: rawConfig?.apiKey,
13
15
  entityId: rawConfig?.entityId,
16
+ projectId: rawConfig?.projectId,
14
17
  };
15
18
  if (!config.apiKey || !config.entityId) {
16
19
  api.logger.warn(`${PLUGIN_CONFIG.LOG_PREFIX} Missing apiKey or entityId in config. Plugin disabled.`);
17
20
  return;
18
21
  }
19
22
  const logger = new MemoriLogger(api);
23
+ const skillsContent = loadSkillsContent(api.resolvePath.bind(api));
20
24
  logger.info(`\n=== ${PLUGIN_CONFIG.LOG_PREFIX} INITIALIZING PLUGIN ===`);
21
25
  logger.info(`${PLUGIN_CONFIG.LOG_PREFIX} Tracking Entity ID: ${config.entityId}`);
22
- api.on('before_prompt_build', (event, ctx) => handleRecall(event, ctx, config, logger));
26
+ if (skillsContent) {
27
+ api.on('before_prompt_build', () => ({ appendSystemContext: skillsContent }));
28
+ }
23
29
  api.on('agent_end', (event, ctx) => handleAugmentation(event, ctx, config, logger));
30
+ registerAllTools({ api, config, logger });
24
31
  },
25
32
  };
26
33
  export default memoriPlugin;
@@ -1,2 +1,3 @@
1
1
  export declare function isSystemMessage(text: string): boolean;
2
+ export declare function extractContentType(rawContent: unknown): string;
2
3
  export declare function cleanText(rawContent: unknown): string;
package/dist/sanitizer.js CHANGED
@@ -19,7 +19,7 @@ export function isSystemMessage(text) {
19
19
  *
20
20
  * OpenClaw wraps metadata in markdown code fences (triple tick).
21
21
  * The actual message is always after the LAST closing fence.
22
- * The message might aslo contain a timestamp prefix like: [Day YYYY-MM-DD HH:MM TZ], that will need to be removed
22
+ * The message might also contain a timestamp prefix like: [Day YYYY-MM-DD HH:MM TZ], that will need to be removed
23
23
  */
24
24
  function extractRawUserMessage(content) {
25
25
  let message = content;
@@ -49,11 +49,19 @@ function extractMessageText(content) {
49
49
  }
50
50
  return '';
51
51
  }
52
+ export function extractContentType(rawContent) {
53
+ if (typeof rawContent === 'string' || !rawContent)
54
+ return 'text';
55
+ if (isMessageBlockArray(rawContent)) {
56
+ const primary = rawContent.find((block) => (block.type === 'text' || typeof block.text === 'string') && block.text);
57
+ return primary?.type ?? 'text';
58
+ }
59
+ return 'text';
60
+ }
52
61
  export function cleanText(rawContent) {
53
62
  let text = extractMessageText(rawContent);
54
63
  if (!text)
55
64
  return '';
56
65
  text = extractRawUserMessage(text);
57
- text = text.replace(/<memori_context>[\s\S]*?<\/memori_context>\s*/g, '');
58
66
  return text.trim();
59
67
  }
@@ -0,0 +1,3 @@
1
+ import type { ToolDeps } from './types.js';
2
+ export declare function registerAllTools(deps: ToolDeps): void;
3
+ export type { ToolDeps };
@@ -0,0 +1,8 @@
1
+ import { createMemoriRecallTool } from './memori-recall.js';
2
+ import { createMemoriRecallSummaryTool } from './memori-recall-summary.js';
3
+ import { createMemoriFeedbackTool } from './memori-feedback.js';
4
+ export function registerAllTools(deps) {
5
+ deps.api.registerTool(createMemoriRecallTool(deps));
6
+ deps.api.registerTool(createMemoriRecallSummaryTool(deps));
7
+ deps.api.registerTool(createMemoriFeedbackTool(deps));
8
+ }
@@ -0,0 +1,25 @@
1
+ import type { ToolDeps } from './types.js';
2
+ export declare function createMemoriFeedbackTool(deps: ToolDeps): {
3
+ name: string;
4
+ label: string;
5
+ description: string;
6
+ parameters: {
7
+ type: string;
8
+ properties: {
9
+ content: {
10
+ type: string;
11
+ description: string;
12
+ };
13
+ };
14
+ required: string[];
15
+ };
16
+ execute(_toolCallId: string, params: {
17
+ content: string;
18
+ }): Promise<{
19
+ content: {
20
+ type: "text";
21
+ text: string;
22
+ }[];
23
+ details: null;
24
+ }>;
25
+ };
@@ -0,0 +1,40 @@
1
+ import { createRecallClient } from '../utils/memori-client.js';
2
+ export function createMemoriFeedbackTool(deps) {
3
+ const { config, logger } = deps;
4
+ return {
5
+ name: 'memori_feedback',
6
+ label: 'Memori Feedback',
7
+ description: 'CRITICAL: You MUST use this tool immediately whenever the user asks you to send feedback, report a bug, suggest a feature, or complain about Memori. Send feedback directly to the Memori team (positive or negative).',
8
+ parameters: {
9
+ type: 'object',
10
+ properties: {
11
+ content: {
12
+ type: 'string',
13
+ description: 'REQUIRED: The feedback message to send.',
14
+ },
15
+ },
16
+ required: ['content'],
17
+ },
18
+ async execute(_toolCallId, params) {
19
+ try {
20
+ logger.info(`memori_feedback sending: ${params.content}`);
21
+ const client = createRecallClient(config.apiKey, config.entityId);
22
+ await client.agentFeedback(params.content);
23
+ const result = { success: true, message: 'Feedback sent successfully.' };
24
+ return {
25
+ content: [{ type: 'text', text: JSON.stringify(result) }],
26
+ details: null,
27
+ };
28
+ }
29
+ catch (e) {
30
+ logger.warn(`memori_feedback failed: ${String(e)}`);
31
+ const errorResult = { error: 'Feedback failed to send.' };
32
+ logger.info(`memori_feedback error result: ${JSON.stringify(errorResult)}`);
33
+ return {
34
+ content: [{ type: 'text', text: JSON.stringify(errorResult) }],
35
+ details: null,
36
+ };
37
+ }
38
+ },
39
+ };
40
+ }
@@ -0,0 +1,39 @@
1
+ import type { ToolDeps } from './types.js';
2
+ export declare function createMemoriRecallSummaryTool(deps: ToolDeps): {
3
+ name: string;
4
+ label: string;
5
+ description: string;
6
+ parameters: {
7
+ type: string;
8
+ properties: {
9
+ dateStart: {
10
+ type: string;
11
+ description: string;
12
+ };
13
+ dateEnd: {
14
+ type: string;
15
+ description: string;
16
+ };
17
+ projectId: {
18
+ type: string;
19
+ description: string;
20
+ };
21
+ sessionId: {
22
+ type: string;
23
+ description: string;
24
+ };
25
+ };
26
+ };
27
+ execute(_toolCallId: string, params: {
28
+ dateStart?: string;
29
+ dateEnd?: string;
30
+ projectId?: string;
31
+ sessionId?: string;
32
+ }): Promise<{
33
+ content: {
34
+ type: "text";
35
+ text: string;
36
+ }[];
37
+ details: null;
38
+ }>;
39
+ };
@@ -0,0 +1,58 @@
1
+ import { createRecallClient } from '../utils/memori-client.js';
2
+ export function createMemoriRecallSummaryTool(deps) {
3
+ const { config, logger } = deps;
4
+ return {
5
+ name: 'memori_recall_summary',
6
+ label: 'Recall Memory Summary',
7
+ description: 'CRITICAL: You MUST use this tool BEFORE answering any requests for a summary, status update, daily brief, or high-level overview of a project or past sessions. Fetch summarized views of stored memories from Memori within a specific date range.',
8
+ parameters: {
9
+ type: 'object',
10
+ properties: {
11
+ dateStart: {
12
+ type: 'string',
13
+ description: 'ISO 8601 (MUST be UTC) date string to filter summaries created on or after this time',
14
+ },
15
+ dateEnd: {
16
+ type: 'string',
17
+ description: 'ISO 8601 (MUST be UTC) date string to filter summaries created on or before this time',
18
+ },
19
+ projectId: {
20
+ type: 'string',
21
+ description: 'CRITICAL: Leave this EMPTY to use the configured default project. ONLY provide a value if the user explicitly asks to search a different project by name.',
22
+ },
23
+ sessionId: {
24
+ type: 'string',
25
+ description: 'Filter to a specific session. Cannot be used without projectId.',
26
+ },
27
+ },
28
+ },
29
+ async execute(_toolCallId, params) {
30
+ try {
31
+ const finalParams = { projectId: config.projectId, ...params };
32
+ if (finalParams.sessionId && !finalParams.projectId) {
33
+ const errorResult = { error: 'sessionId cannot be provided without projectId' };
34
+ logger.warn(`memori_recall_summary rejected: ${JSON.stringify(errorResult)}`);
35
+ return {
36
+ content: [{ type: 'text', text: JSON.stringify(errorResult) }],
37
+ details: null,
38
+ };
39
+ }
40
+ logger.info(`memori_recall_summary params: ${JSON.stringify(finalParams)}`);
41
+ const client = createRecallClient(config.apiKey, config.entityId);
42
+ const result = await client.agentRecallSummary(finalParams);
43
+ return {
44
+ content: [{ type: 'text', text: JSON.stringify(result) }],
45
+ details: null,
46
+ };
47
+ }
48
+ catch (e) {
49
+ logger.warn(`memori_recall_summary failed: ${String(e)}`);
50
+ const errorResult = { error: 'Recall summary failed' };
51
+ return {
52
+ content: [{ type: 'text', text: JSON.stringify(errorResult) }],
53
+ details: null,
54
+ };
55
+ }
56
+ },
57
+ };
58
+ }
@@ -0,0 +1,61 @@
1
+ import type { ToolDeps } from './types.js';
2
+ export declare function createMemoriRecallTool(deps: ToolDeps): {
3
+ name: string;
4
+ label: string;
5
+ description: string;
6
+ parameters: {
7
+ type: string;
8
+ properties: {
9
+ query: {
10
+ type: string;
11
+ description: string;
12
+ };
13
+ limit: {
14
+ type: string;
15
+ description: string;
16
+ };
17
+ dateStart: {
18
+ type: string;
19
+ description: string;
20
+ };
21
+ dateEnd: {
22
+ type: string;
23
+ description: string;
24
+ };
25
+ projectId: {
26
+ type: string;
27
+ description: string;
28
+ };
29
+ sessionId: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ signal: {
34
+ type: string;
35
+ description: string;
36
+ enum: string[];
37
+ };
38
+ source: {
39
+ type: string;
40
+ description: string;
41
+ enum: string[];
42
+ };
43
+ };
44
+ required: string[];
45
+ };
46
+ execute(_toolCallId: string, params: {
47
+ query: string;
48
+ dateStart?: string;
49
+ dateEnd?: string;
50
+ projectId?: string;
51
+ sessionId?: string;
52
+ signal?: string;
53
+ source?: string;
54
+ }): Promise<{
55
+ content: {
56
+ type: "text";
57
+ text: string;
58
+ }[];
59
+ details: null;
60
+ }>;
61
+ };
@@ -0,0 +1,99 @@
1
+ import { createRecallClient } from '../utils/memori-client.js';
2
+ export function createMemoriRecallTool(deps) {
3
+ const { config, logger } = deps;
4
+ return {
5
+ name: 'memori_recall',
6
+ label: 'Recall Memory',
7
+ description: 'CRITICAL: You MUST use this tool to search for past context BEFORE claiming you do not know the user, their preferences, or past events. Explicitly fetch relevant memories from Memori using filters...',
8
+ parameters: {
9
+ type: 'object',
10
+ properties: {
11
+ query: {
12
+ type: 'string',
13
+ description: 'REQUIRED: The natural language search query to find specific facts (e.g., "What database did we decide to use?", "Ryan\'s dogs"). DO NOT use wildcards like "*" or regex. This is a semantic search, so use real words.',
14
+ },
15
+ limit: {
16
+ type: 'number',
17
+ description: 'Maximum number of memories to return (default: 10)',
18
+ },
19
+ dateStart: {
20
+ type: 'string',
21
+ description: 'ISO 8601 (MUST be UTC) date string to filter memories created on or after this time',
22
+ },
23
+ dateEnd: {
24
+ type: 'string',
25
+ description: 'ISO 8601 (MUST be UTC) date string to filter memories created on or before this time',
26
+ },
27
+ projectId: {
28
+ type: 'string',
29
+ description: 'CRITICAL: Leave this EMPTY to use the configured default project. ONLY provide a value if the user explicitly asks to search a different project by name.',
30
+ },
31
+ sessionId: {
32
+ type: 'string',
33
+ description: 'Filter to a specific session. Cannot be used without projectId.',
34
+ },
35
+ signal: {
36
+ type: 'string',
37
+ description: 'Filter to a specific fact signal. MUST be one of the allowed enum values.',
38
+ enum: [
39
+ 'commit',
40
+ 'discovery',
41
+ 'failure',
42
+ 'inference',
43
+ 'pattern',
44
+ 'result',
45
+ 'update',
46
+ 'verification',
47
+ ],
48
+ },
49
+ source: {
50
+ type: 'string',
51
+ description: 'Filter to a specific source origin. MUST be one of the allowed enum values.',
52
+ enum: [
53
+ 'constraint',
54
+ 'decision',
55
+ 'execution',
56
+ 'fact',
57
+ 'insight',
58
+ 'instruction',
59
+ 'status',
60
+ 'strategy',
61
+ 'task',
62
+ ],
63
+ },
64
+ },
65
+ // Force the LLM to ALWAYS provide a search query
66
+ required: ['query'],
67
+ },
68
+ async execute(_toolCallId, params) {
69
+ try {
70
+ // If params.projectId is undefined, it falls back to config.projectId.
71
+ // If the LLM intentionally provides one, it overwrites the config.
72
+ const finalParams = { projectId: config.projectId, ...params };
73
+ if (finalParams.sessionId && !finalParams.projectId) {
74
+ const errorResult = { error: 'sessionId cannot be provided without projectId' };
75
+ logger.warn(`memori_recall rejected: ${JSON.stringify(errorResult)}`);
76
+ return {
77
+ content: [{ type: 'text', text: JSON.stringify(errorResult) }],
78
+ details: null,
79
+ };
80
+ }
81
+ logger.info(`memori_recall params: ${JSON.stringify(finalParams)}`);
82
+ const client = createRecallClient(config.apiKey, config.entityId);
83
+ const result = await client.agentRecall(finalParams);
84
+ return {
85
+ content: [{ type: 'text', text: JSON.stringify(result) }],
86
+ details: null,
87
+ };
88
+ }
89
+ catch (e) {
90
+ logger.warn(`memori_recall failed: ${String(e)}`);
91
+ const errorResult = { error: 'Recall failed' };
92
+ return {
93
+ content: [{ type: 'text', text: JSON.stringify(errorResult) }],
94
+ details: null,
95
+ };
96
+ }
97
+ },
98
+ };
99
+ }
@@ -0,0 +1,8 @@
1
+ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
2
+ import type { MemoriPluginConfig } from '../types.js';
3
+ import type { MemoriLogger } from '../utils/logger.js';
4
+ export interface ToolDeps {
5
+ api: OpenClawPluginApi;
6
+ config: MemoriPluginConfig;
7
+ logger: MemoriLogger;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import { IntegrationMessage } from '@memorilabs/memori/integrations';
1
2
  export interface MemoriPluginConfig {
2
3
  apiKey: string;
3
4
  entityId: string;
5
+ projectId: string;
4
6
  }
5
7
  export interface OpenClawMessageBlock {
6
8
  type?: string;
@@ -44,13 +46,7 @@ export interface ExtractedToolCall {
44
46
  result: unknown;
45
47
  }
46
48
  export interface ParsedTurn {
47
- userMessage: {
48
- role: string;
49
- content: string;
50
- } | null;
51
- assistantMessage: {
52
- role: string;
53
- content: string;
54
- } | null;
49
+ userMessage: IntegrationMessage | null;
50
+ assistantMessage: IntegrationMessage | null;
55
51
  tools: ExtractedToolCall[];
56
52
  }
@@ -6,6 +6,7 @@ export interface ExtractedContext {
6
6
  entityId: string;
7
7
  sessionId: string;
8
8
  provider: string;
9
+ projectId: string;
9
10
  }
10
11
  /**
11
12
  * Extracts and normalizes context information from OpenClaw event and context objects.
@@ -14,7 +15,8 @@ export interface ExtractedContext {
14
15
  * @param event - OpenClaw event object
15
16
  * @param ctx - OpenClaw context object
16
17
  * @param configuredEntityId - Hardcoded entity ID from plugin config
17
- * @returns Normalized context with entityId, sessionId, and provider
18
+ * @param configuredProjectId - Project ID from plugin config
19
+ * @returns Normalized context with entityId, sessionId, provider, and projectId
18
20
  * @throws Error If entityId, sessionId, or provider cannot be determined
19
21
  */
20
- export declare function extractContext(event: OpenClawEvent, ctx: OpenClawContext, configuredEntityId: string): ExtractedContext;
22
+ export declare function extractContext(event: OpenClawEvent, ctx: OpenClawContext, configuredEntityId: string, configuredProjectId: string): ExtractedContext;
@@ -5,10 +5,11 @@
5
5
  * @param event - OpenClaw event object
6
6
  * @param ctx - OpenClaw context object
7
7
  * @param configuredEntityId - Hardcoded entity ID from plugin config
8
- * @returns Normalized context with entityId, sessionId, and provider
8
+ * @param configuredProjectId - Project ID from plugin config
9
+ * @returns Normalized context with entityId, sessionId, provider, and projectId
9
10
  * @throws Error If entityId, sessionId, or provider cannot be determined
10
11
  */
11
- export function extractContext(event, ctx, configuredEntityId) {
12
+ export function extractContext(event, ctx, configuredEntityId, configuredProjectId) {
12
13
  const sessionId = ctx.sessionKey || event.sessionId;
13
14
  const provider = ctx.messageProvider || event.messageProvider;
14
15
  if (!sessionId) {
@@ -21,5 +22,6 @@ export function extractContext(event, ctx, configuredEntityId) {
21
22
  entityId: configuredEntityId,
22
23
  sessionId,
23
24
  provider,
25
+ projectId: configuredProjectId,
24
26
  };
25
27
  }
@@ -1,3 +1,4 @@
1
1
  export { extractContext, type ExtractedContext } from './context.js';
2
2
  export { MemoriLogger } from './logger.js';
3
- export { initializeMemoriClient } from './memori-client.js';
3
+ export { initializeMemoriClient, createRecallClient } from './memori-client.js';
4
+ export { loadSkillsContent } from './skills-loader.js';
@@ -1,3 +1,4 @@
1
1
  export { extractContext } from './context.js';
2
2
  export { MemoriLogger } from './logger.js';
3
- export { initializeMemoriClient } from './memori-client.js';
3
+ export { initializeMemoriClient, createRecallClient } from './memori-client.js';
4
+ export { loadSkillsContent } from './skills-loader.js';
@@ -8,3 +8,14 @@ import { ExtractedContext } from './context.js';
8
8
  * @returns Configured OpenClawIntegration instance
9
9
  */
10
10
  export declare function initializeMemoriClient(apiKey: string, context: ExtractedContext): OpenClawIntegration;
11
+ /**
12
+ * Creates a minimal Memori client scoped only to an entity, with no session or project
13
+ * context pre-set. Intended for use in tool execute handlers where OpenClaw does not
14
+ * reliably provide session context — callers supply projectId/sessionId as explicit
15
+ * parameters instead.
16
+ *
17
+ * @param apiKey - Memori API key
18
+ * @param entityId - Entity ID for attribution
19
+ * @returns Configured OpenClawIntegration instance
20
+ */
21
+ export declare function createRecallClient(apiKey: string, entityId: string): OpenClawIntegration;
@@ -11,7 +11,25 @@ export function initializeMemoriClient(apiKey, context) {
11
11
  const memori = new Memori();
12
12
  memori.config.apiKey = apiKey;
13
13
  const openclaw = memori.integrate(OpenClawIntegration);
14
- openclaw.setAttribution(context.entityId, context.provider);
15
- openclaw.setSession(context.sessionId);
14
+ openclaw
15
+ .scope(context.sessionId, context.projectId)
16
+ .attribution(context.entityId, context.provider);
17
+ return openclaw;
18
+ }
19
+ /**
20
+ * Creates a minimal Memori client scoped only to an entity, with no session or project
21
+ * context pre-set. Intended for use in tool execute handlers where OpenClaw does not
22
+ * reliably provide session context — callers supply projectId/sessionId as explicit
23
+ * parameters instead.
24
+ *
25
+ * @param apiKey - Memori API key
26
+ * @param entityId - Entity ID for attribution
27
+ * @returns Configured OpenClawIntegration instance
28
+ */
29
+ export function createRecallClient(apiKey, entityId) {
30
+ const memori = new Memori();
31
+ memori.config.apiKey = apiKey;
32
+ const openclaw = memori.integrate(OpenClawIntegration);
33
+ openclaw.attribution(entityId);
16
34
  return openclaw;
17
35
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Loads the Memori skills document at plugin registration time.
3
+ * Returns an empty string if the file cannot be read so the plugin
4
+ * degrades gracefully rather than failing to register.
5
+ */
6
+ export declare function loadSkillsContent(resolvePath: (input: string) => string): string;
@@ -0,0 +1,14 @@
1
+ import { readFileSync } from 'fs';
2
+ /**
3
+ * Loads the Memori skills document at plugin registration time.
4
+ * Returns an empty string if the file cannot be read so the plugin
5
+ * degrades gracefully rather than failing to register.
6
+ */
7
+ export function loadSkillsContent(resolvePath) {
8
+ try {
9
+ return readFileSync(resolvePath('skills/memori/skills.md'), 'utf-8');
10
+ }
11
+ catch {
12
+ return '';
13
+ }
14
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.0.7";
1
+ export declare const SDK_VERSION = "0.0.9";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '0.0.7';
1
+ export const SDK_VERSION = '0.0.9';
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "id": "openclaw-memori",
3
3
  "name": "Memori System",
4
- "version": "0.0.2",
4
+ "version": "0.0.8",
5
5
  "description": "Hosted memory backend",
6
6
  "kind": "memory",
7
7
  "main": "dist/index.js",
8
+ "contracts": {
9
+ "tools": ["memori_recall", "memori_recall_summary", "memori_feedback"]
10
+ },
8
11
  "uiHints": {
9
12
  "apiKey": {
10
13
  "label": "Memori API Key",
@@ -16,6 +19,11 @@
16
19
  "label": "Entity ID",
17
20
  "placeholder": "e.g., your-app-user-id",
18
21
  "help": "Required. The unique identifier to attribute these memories to."
22
+ },
23
+ "projectId": {
24
+ "label": "Project ID",
25
+ "placeholder": "e.g., my-project",
26
+ "help": "Required. Scopes all memories to this project."
19
27
  }
20
28
  },
21
29
  "configSchema": {
@@ -29,7 +37,12 @@
29
37
  "entityId": {
30
38
  "type": "string",
31
39
  "title": "Entity ID",
32
- "description": "Required. Hardcode a specific Entity ID for memories."
40
+ "description": "Required. The unique identifier to attribute these memories to."
41
+ },
42
+ "projectId": {
43
+ "type": "string",
44
+ "title": "Project ID",
45
+ "description": "Required. Scopes all memories to this project."
33
46
  }
34
47
  },
35
48
  "required": []
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@memorilabs/openclaw-memori",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Official MemoriLabs.ai long-term memory plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
+ "skills",
10
11
  "openclaw.plugin.json"
11
12
  ],
12
13
  "scripts": {
@@ -66,6 +67,6 @@
66
67
  "@hono/node-server": "^1.19.10"
67
68
  },
68
69
  "dependencies": {
69
- "@memorilabs/memori": "^0.0.8"
70
+ "@memorilabs/memori": "0.1.11-beta"
70
71
  }
71
72
  }
@@ -0,0 +1,65 @@
1
+ ## Memori — Your Persistent Memory Layer
2
+
3
+ You have access to Memori, a structured long-term memory backend.
4
+
5
+ **Automatic augmentation** (`agent_end`): After you respond, the conversation turn is automatically sent to Memori to extract and store facts, preferences, decisions, and relationships for future sessions. You do not need to do this manually.
6
+
7
+ **Manual Recall (IMPORTANT)**: You do NOT automatically receive context from past sessions.
8
+ **RULE:** You must NEVER say "I don't know" about the user, their preferences, or past events without FIRST running a `memori_recall` search to check if you remember it. If a user asks a question about themselves, you MUST search Memori before responding.
9
+
10
+ ---
11
+
12
+ ### Memory Retrieval Tools
13
+
14
+ Use these to search your memory explicitly:
15
+
16
+ **`memori_recall`** — Fetch granular memory facts using a search query and optional filters. Use this when you need specific details (e.g., "what database did we choose?").
17
+
18
+ | Parameter | Type | Description |
19
+ | ----------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | `query` | string | **Required.** A natural language semantic search query (e.g. "dogs"). **DO NOT use wildcards like `*`.** |
21
+ | `dateStart` | string | ISO 8601 (MUST be UTC) — memories on or after this time |
22
+ | `dateEnd` | string | ISO 8601 (MUST be UTC) — memories on or before this time |
23
+ | `projectId` | string | CRITICAL: Leave EMPTY to use the current project. ONLY provide a value if the user explicitly names a different project. |
24
+ | `sessionId` | string | Scope to a specific session — **requires `projectId`** |
25
+ | `signal` | string | Filter by signal type. Allowed values: `commit`, `discovery`, `failure`, `inference`, `pattern`, `result`, `update`, `verification`. |
26
+ | `source` | string | Filter by source origin. Allowed values: `constraint`, `decision`, `execution`, `fact`, `insight`, `instruction`, `status`, `strategy`, `task`. |
27
+
28
+ **`memori_recall_summary`** — Fetch summarized views of stored memories. **RULE:** You must NEVER guess or make up a status update, daily brief, or project overview. If the user asks "what did we do last time" or "give me a summary", you MUST use this tool before answering.
29
+
30
+ | Parameter | Type | Description |
31
+ | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------ |
32
+ | `dateStart` | string | ISO 8601 (MUST be UTC) — summaries on or after this time |
33
+ | `dateEnd` | string | ISO 8601 (MUST be UTC) — summaries on or before this time |
34
+ | `projectId` | string | CRITICAL: Leave EMPTY to use the current project. ONLY provide a value if the user explicitly names a different project. |
35
+ | `sessionId` | string | Scope to a specific session — **requires `projectId`** |
36
+
37
+ > `sessionId` cannot be used without `projectId`. The backend will reject it.
38
+
39
+ ---
40
+
41
+ ### Feedback Tool
42
+
43
+ **RULE:** You must ALWAYS use this tool immediately if the user asks you to send feedback, report a bug, suggest a feature, or complains about the system. Do NOT just say "I will let the developers know"—you must actually execute the tool to send the message.
44
+
45
+ **`memori_feedback`** — Send feedback, suggestions, or issues directly to the Memori team.
46
+
47
+ | Parameter | Type | Description |
48
+ | --------- | ------ | ------------------------------------------------------------------------ |
49
+ | `content` | string | **Required.** The feedback text to send (positive, negative, bugs, etc.) |
50
+
51
+ ### Memory Scoping
52
+
53
+ All memories are scoped to the current `entityId` and `projectId`. The current project is applied by default — you only need to pass `projectId` when explicitly overriding it for a cross-project lookup.
54
+
55
+ ---
56
+
57
+ ### Coexistence With File Memory
58
+
59
+ Memori works alongside local file memory (e.g., `MEMORY.md`), it does not replace it:
60
+
61
+ | Layer | Scope | Lifetime |
62
+ | ------------------------- | ------------------------------------- | --------------------------- |
63
+ | Session context | Current conversation | Dies with session |
64
+ | File memory (`MEMORY.md`) | Curated strategic facts | Persistent on disk |
65
+ | Memori | Auto-extracted facts, knowledge graph | Cloud — survives compaction |
@@ -1,5 +0,0 @@
1
- import { OpenClawEvent, OpenClawContext, MemoriPluginConfig } from '../types.js';
2
- import { MemoriLogger } from '../utils/index.js';
3
- export declare function handleRecall(event: OpenClawEvent, ctx: OpenClawContext, config: MemoriPluginConfig, logger: MemoriLogger): Promise<{
4
- prependContext: string;
5
- } | undefined>;
@@ -1,34 +0,0 @@
1
- import { cleanText, isSystemMessage } from '../sanitizer.js';
2
- import { RECALL_CONFIG } from '../constants.js';
3
- import { extractContext, initializeMemoriClient } from '../utils/index.js';
4
- export async function handleRecall(event, ctx, config, logger) {
5
- logger.section('RECALL HOOK START');
6
- try {
7
- const context = extractContext(event, ctx, config.entityId);
8
- const promptText = cleanText(event.prompt);
9
- if (!promptText ||
10
- promptText.length < RECALL_CONFIG.MIN_PROMPT_LENGTH ||
11
- isSystemMessage(promptText)) {
12
- logger.info('Prompt too short or is a system message. Aborting recall.');
13
- return undefined;
14
- }
15
- const memoriClient = initializeMemoriClient(config.apiKey, context);
16
- logger.info('Executing SDK Recall...');
17
- const recallText = await memoriClient.recall(promptText);
18
- const hookReturn = recallText ? { prependContext: recallText } : undefined;
19
- if (hookReturn) {
20
- logger.info('Successfully injected memory context.');
21
- }
22
- else {
23
- logger.info('No relevant memories found.');
24
- }
25
- return hookReturn;
26
- }
27
- catch (err) {
28
- logger.error(`Recall failed: ${err instanceof Error ? err.message : String(err)}`);
29
- return undefined;
30
- }
31
- finally {
32
- logger.endSection('RECALL HOOK END');
33
- }
34
- }