@memorilabs/openclaw-memori 0.0.8 → 0.0.10

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.
Files changed (40) hide show
  1. package/README.md +118 -91
  2. package/dist/cli/commands.d.ts +2 -0
  3. package/dist/cli/commands.js +143 -0
  4. package/dist/cli/config-file.d.ts +8 -0
  5. package/dist/cli/config-file.js +64 -0
  6. package/dist/handlers/augmentation.js +12 -5
  7. package/dist/index.js +10 -3
  8. package/dist/sanitizer.d.ts +1 -0
  9. package/dist/sanitizer.js +10 -2
  10. package/dist/tools/index.d.ts +3 -0
  11. package/dist/tools/index.js +12 -0
  12. package/dist/tools/memori-feedback.d.ts +25 -0
  13. package/dist/tools/memori-feedback.js +40 -0
  14. package/dist/tools/memori-quota.d.ts +17 -0
  15. package/dist/tools/memori-quota.js +55 -0
  16. package/dist/tools/memori-recall-summary.d.ts +39 -0
  17. package/dist/tools/memori-recall-summary.js +58 -0
  18. package/dist/tools/memori-recall.d.ts +61 -0
  19. package/dist/tools/memori-recall.js +99 -0
  20. package/dist/tools/memori-signup.d.ts +25 -0
  21. package/dist/tools/memori-signup.js +72 -0
  22. package/dist/tools/types.d.ts +8 -0
  23. package/dist/tools/types.js +1 -0
  24. package/dist/types.d.ts +4 -8
  25. package/dist/utils/context.d.ts +4 -2
  26. package/dist/utils/context.js +4 -2
  27. package/dist/utils/index.d.ts +2 -1
  28. package/dist/utils/index.js +2 -1
  29. package/dist/utils/memori-client.d.ts +11 -0
  30. package/dist/utils/memori-client.js +20 -2
  31. package/dist/utils/skills-loader.d.ts +6 -0
  32. package/dist/utils/skills-loader.js +14 -0
  33. package/dist/version.d.ts +1 -1
  34. package/dist/version.js +1 -1
  35. package/openclaw.plugin.json +15 -2
  36. package/package.json +3 -2
  37. package/skills/clawhub/SKILL.md +313 -0
  38. package/skills/memori/SKILL.md +284 -0
  39. package/dist/handlers/recall.d.ts +0 -5
  40. package/dist/handlers/recall.js +0 -34
package/README.md CHANGED
@@ -25,154 +25,181 @@
25
25
 
26
26
  ---
27
27
 
28
- ## Why Memori for OpenClaw?
28
+ # Memori for OpenClaw
29
29
 
30
- OpenClaw ships with a simple file-first memory system designed for lightweight experimentation. As deployments scale into production environments, teams often run into memory problems that need more structured, deterministic infrastructure.
30
+ Memori gives OpenClaw agents a structured, long-term memory system. It automatically captures what happens and lets agents recall it on demand so context survives across sessions without bloating the prompt.
31
31
 
32
- Memori provides a drop-in memory layer purpose-built for agentic systems running OpenClaw in production. It works through OpenClaw's plugin lifecycle, so you get persistent, structured memory without changing your agent logic.
32
+ Instead of relying solely on natural-language memory, Memori structures persistent memory from both conversation and agent trace the agent's actions, tool results, decisions, and outcomes — so it can recall what actually happened when it matters.
33
33
 
34
- ## Common Challenges with Default OpenClaw Memory
34
+ ---
35
+
36
+ ## The problem
35
37
 
36
- ### 1. Fact conflicts in long-running agents
38
+ OpenClaw's default memory works for simple use cases, but breaks at scale:
37
39
 
38
- OpenClaw stores memory as plain markdown files. When facts change or contradict over time, there is no deterministic conflict resolution or lifecycle management.
40
+ - Memory is stored as flat markdown files
41
+ - Context is lost due to compaction
42
+ - Important decisions and constraints disappear
43
+ - No relationships between facts
44
+ - Memory bleeds across users and projects
39
45
 
40
- Memori introduces structured memory with update logic, decay policies, and deterministic fact handling.
46
+ ---
41
47
 
42
- ### 2. Context loss from token limits
48
+ ## What Memori changes
43
49
 
44
- As sessions grow, context must be compacted to fit within model token limits. Important details can be dropped during compression.
50
+ Memori replaces flat memory with structured, scoped memory built from:
45
51
 
46
- Memori stores memory outside the prompt and retrieves the right facts at query time, eliminating compaction loss.
52
+ - Agent execution (tool calls, results, decisions, outcomes)
47
53
 
48
- ### 3. No relationship reasoning
54
+ Instead of replaying history, agents retrieve exactly what they need.
49
55
 
50
- OpenClaw retrieves semantically similar text but does not model relationships between entities.
56
+ ---
51
57
 
52
- Memori builds structured memory graphs that let agents reason across linked facts, not just retrieve similar chunks.
58
+ ## How it works
53
59
 
54
- ### 4. Cross-project noise
60
+ Memori runs on two parallel systems:
55
61
 
56
- When multiple projects share memory storage, irrelevant context can bleed across workflows.
62
+ ### 1. Advanced augmentation
57
63
 
58
- Memori supports scoped memory namespaces to isolate projects and workflows.
64
+ After each interaction, Memori converts raw session data into structured, reusable memories asynchronously.
59
65
 
60
- ### 5. No user-level isolation
66
+ - Transforms raw agent sessions into structured memory units
67
+ - Captures the agent's actions, reasoning, tool usage, responses, corrections, and failures
68
+ - Organizes into classes to enable efficient retrieval
69
+ - Generates embeddings for semantic retrieval
70
+ - Updates structured memory and the knowledge graph
61
71
 
62
- Default memory systems do not provide deterministic isolation across users.
72
+ This is how structured memory is continuously built and updated over time.
63
73
 
64
- Memori enforces user-scoped memory boundaries for secure multi-user deployments.
74
+ It runs **after the agent responds** and does not impact latency.
65
75
 
66
- ## What Changes When You Add Memori?
76
+ ---
67
77
 
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.
78
+ ### 2. Agent-controlled recall
69
79
 
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. |
80
+ Recall is **explicit and initiated by the agent**.
76
81
 
77
- The plugin still remains drop-in: OpenClaw handles the agent loop, while Memori adds recall, augmentation, sanitization, and observability around it.
82
+ Memori separates memory creation from memory recall:
78
83
 
84
+ - Creation is automatic (advanced augmentation)
85
+ - Recall is intentional (agent-controlled)
79
86
 
80
- ## Quickstart
87
+ Agents decide:
81
88
 
82
- Get persistent memory running in your OpenClaw gateway in three steps.
89
+ - When to recall
90
+ - What scope to recall from
91
+ - How much history to include
92
+
93
+ Memori does not automatically inject memory into the prompt. The agent retrieves only the context it needs, keeping token usage efficient.
94
+
95
+ Available tools:
96
+
97
+ - **`memori_recall`** — query structured memory for facts, constraints, decisions, and patterns
98
+ - **`memori_recall_summary`** — retrieve summaries and the daily brief
99
+ - **`memori_feedback`** — report on memory quality to improve the system
100
+
101
+ ---
102
+
103
+ ## Quickstart
83
104
 
84
105
  ### Prerequisites
85
106
 
86
107
  - [OpenClaw](https://openclaw.ai) `v2026.3.2` or later
87
108
  - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai)
88
- - An Entity ID to attribute memories to, such as a user ID, tenant ID, or agent name
89
-
90
- ### 1. Install and Enable
109
+ - An Entity ID to scope memory to a specific user, agent, or system
110
+ - A Project ID to scope memory to a specific project or workspace
91
111
 
92
- Run the following commands in your terminal to install and enable the plugin:
112
+ ### 1. Install
93
113
 
94
114
  ```bash
95
- # 1. Install the plugin from npm
96
115
  openclaw plugins install @memorilabs/openclaw-memori
97
-
98
- # 2. Enable it in your workspace
99
116
  openclaw plugins enable openclaw-memori
100
-
101
- # 3. Restart the OpenClaw gateway
102
- openclaw gateway restart
103
117
  ```
104
118
 
105
119
  ### 2. Configure
106
120
 
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.
121
+ ```bash
122
+ openclaw memori init \
123
+ --api-key "YOUR_MEMORI_API_KEY" \
124
+ --entity-id "your-app-user-id" \
125
+ --project-id "my-project"
126
+ ```
108
127
 
109
- ### Option A: Via OpenClaw CLI (Recommended)
128
+ ### 3. Verify
110
129
 
111
130
  ```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"
131
+ openclaw gateway restart
132
+ openclaw memori status --check
114
133
  ```
115
134
 
116
- ### Option B: Via `openclaw.json`
117
-
118
- Add the following to your `~/.openclaw/openclaw.json` file:
119
-
120
- ```json
121
- {
122
- "plugins": {
123
- "entries": {
124
- "openclaw-memori": {
125
- "enabled": true,
126
- "config": {
127
- "apiKey": "your-memori-api-key",
128
- "entityId": "your-app-user-id"
129
- }
130
- }
131
- }
132
- }
133
- }
135
+ Expected:
136
+
137
+ ```
138
+ Status: Ready
134
139
  ```
135
140
 
136
- ### Configuration Options
141
+ ### 4. Test the memory loop
137
142
 
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. |
143
+ 1. Tell the agent something durable:
142
144
 
143
- ### 3. Verify
145
+ > "I always use TypeScript and prefer functional patterns."
144
146
 
145
- Restart the gateway and inspect the logs:
147
+ 2. Start a new session and ask:
146
148
 
147
- ```bash
148
- openclaw gateway restart
149
- openclaw gateway logs --filter "[Memori]"
150
- ```
149
+ > "Write a hello world script in my preferred language."
151
150
 
152
- You should see:
151
+ 3. Confirm the agent used `memori_recall` to fetch your preferences:
152
+ ```
153
+ [Memori] memori_recall params: {"projectId":"my-project","query":"preferred programming language"}
154
+ ```
153
155
 
154
- ```text
155
- [Memori] === INITIALIZING PLUGIN ===
156
- [Memori] Tracking Entity ID: your-app-user-id
157
- ```
156
+ If it works, you now have persistent memory across sessions.
157
+
158
+ ---
159
+
160
+ ## Memory model
161
+
162
+ Memory is scoped to prevent noise and ensure relevance:
163
+
164
+ - `entity_id` → user, agent, or system context
165
+ - `project_id` → project or workspace context
166
+ - `session_id` → specific session (requires `project_id`)
167
+ - `date_start` / `date_end` → time-bounded recall (defaults to all-time if omitted)
168
+ - `source` → type of memory (recall only)
169
+ - `signal` → how the memory was derived (recall only)
170
+
171
+ All timestamps are stored in **UTC**.
158
172
 
159
- To test the full memory loop:
173
+ ---
174
+
175
+ ## Agent behavior (read this)
176
+
177
+ Agents should:
160
178
 
161
- 1. Send a message with a durable preference:
162
- `I always use TypeScript and prefer functional patterns.`
163
- 2. Confirm augmentation ran:
164
- `Augmentation successful!`
165
- 3. Start a new session and ask:
166
- `Write a hello world script.`
167
- 4. Confirm recall ran:
168
- `Successfully injected memory context.`
179
+ - Retrieve a summary at the start of meaningful sessions
180
+ - Use targeted recall (not broad queries)
181
+ - Avoid recalling on every turn
182
+ - Use memory only when context is needed
183
+ - Send feedback when memory is missing or incorrect
184
+
185
+ See SKILL.md for full behavior guidelines.
186
+
187
+ ---
169
188
 
170
- ## How It Works
189
+ ## Typical workflow
171
190
 
172
- This plugin integrates with OpenClaw's event lifecycle to provide persistent memory without interfering with the agent's core logic:
191
+ 1. Start session retrieve summary
192
+ 2. During task → targeted recall
193
+ 3. Missing context → send feedback
194
+ 4. End of session → memory is captured automatically
173
195
 
174
- 1. **`before_prompt_build` (Intelligent Recall):** When a user sends a message, the plugin intercepts the event, queries the Memori API, and safely prepends relevant memories to the agent's system context.
175
- 2. **`agent_end` (Advanced Augmentation):** Once the agent finishes generating its response, the plugin captures the final `user` and `assistant` messages, sanitizes them, and sends them to the Memori integration endpoint for long-term storage and entity mapping.
196
+ ---
197
+
198
+ ## Multi-agent ready
199
+
200
+ The plugin is fully stateless and thread-safe. You can run it across multiple agents in the same gateway without shared state or concurrency issues.
201
+
202
+ ---
176
203
 
177
204
  ## Contributing
178
205
 
@@ -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,12 @@
1
+ import { createMemoriSignupTool } from './memori-signup.js';
2
+ import { createMemoriQuotaTool } from './memori-quota.js';
3
+ import { createMemoriRecallTool } from './memori-recall.js';
4
+ import { createMemoriRecallSummaryTool } from './memori-recall-summary.js';
5
+ import { createMemoriFeedbackTool } from './memori-feedback.js';
6
+ export function registerAllTools(deps) {
7
+ deps.api.registerTool(createMemoriSignupTool(deps));
8
+ deps.api.registerTool(createMemoriQuotaTool(deps));
9
+ deps.api.registerTool(createMemoriRecallTool(deps));
10
+ deps.api.registerTool(createMemoriRecallSummaryTool(deps));
11
+ deps.api.registerTool(createMemoriFeedbackTool(deps));
12
+ }
@@ -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
+ };