@memorilabs/openclaw-memori 0.0.5-beta → 0.0.6
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 +95 -13
- package/dist/constants.d.ts +12 -1
- package/dist/constants.js +12 -1
- package/dist/handlers/augmentation.d.ts +4 -0
- package/dist/handlers/augmentation.js +143 -49
- package/dist/handlers/recall.js +0 -2
- package/dist/types.d.ts +23 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<i>
|
|
8
|
+
<i>Give OpenClaw persistent, structured memory with Memori. Capture what matters, recall it when relevant, and move from lightweight experimentation to production-ready memory infrastructure.</i>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
@@ -25,14 +25,69 @@
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Why Memori for OpenClaw?
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
- **Auto-Capture:** After the agent responds, the plugin securely sends the exchange to Memori to extract new facts, update stale ones, and merge duplicates.
|
|
32
|
-
- **Bulletproof Sanitization:** Automatically strips OpenClaw system metadata, internal timestamps, and thinking blocks to prevent context pollution and feedback loops.
|
|
33
|
-
- **Stateless & Thread-Safe:** A completely stateless architecture ensures zero memory leaks and 100% thread safety for multi-agent OpenClaw gateways.
|
|
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.
|
|
34
31
|
|
|
35
|
-
|
|
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.
|
|
33
|
+
|
|
34
|
+
## Common Challenges with Default OpenClaw Memory
|
|
35
|
+
|
|
36
|
+
### 1. Fact conflicts in long-running agents
|
|
37
|
+
|
|
38
|
+
OpenClaw stores memory as plain markdown files. When facts change or contradict over time, there is no deterministic conflict resolution or lifecycle management.
|
|
39
|
+
|
|
40
|
+
Memori introduces structured memory with update logic, decay policies, and deterministic fact handling.
|
|
41
|
+
|
|
42
|
+
### 2. Context loss from token limits
|
|
43
|
+
|
|
44
|
+
As sessions grow, context must be compacted to fit within model token limits. Important details can be dropped during compression.
|
|
45
|
+
|
|
46
|
+
Memori stores memory outside the prompt and retrieves the right facts at query time, eliminating compaction loss.
|
|
47
|
+
|
|
48
|
+
### 3. No relationship reasoning
|
|
49
|
+
|
|
50
|
+
OpenClaw retrieves semantically similar text but does not model relationships between entities.
|
|
51
|
+
|
|
52
|
+
Memori builds structured memory graphs that let agents reason across linked facts, not just retrieve similar chunks.
|
|
53
|
+
|
|
54
|
+
### 4. Cross-project noise
|
|
55
|
+
|
|
56
|
+
When multiple projects share memory storage, irrelevant context can bleed across workflows.
|
|
57
|
+
|
|
58
|
+
Memori supports scoped memory namespaces to isolate projects and workflows.
|
|
59
|
+
|
|
60
|
+
### 5. No user-level isolation
|
|
61
|
+
|
|
62
|
+
Default memory systems do not provide deterministic isolation across users.
|
|
63
|
+
|
|
64
|
+
Memori enforces user-scoped memory boundaries for secure multi-user deployments.
|
|
65
|
+
|
|
66
|
+
## What Changes When You Add Memori?
|
|
67
|
+
|
|
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
|
+
|
|
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
|
+
|
|
77
|
+
The plugin still remains drop-in: OpenClaw handles the agent loop, while Memori adds recall, augmentation, sanitization, and observability around it.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Quickstart
|
|
81
|
+
|
|
82
|
+
Get persistent memory running in your OpenClaw gateway in three steps.
|
|
83
|
+
|
|
84
|
+
### Prerequisites
|
|
85
|
+
|
|
86
|
+
- [OpenClaw](https://openclaw.ai) `v2026.3.2` or later
|
|
87
|
+
- 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
|
|
36
91
|
|
|
37
92
|
Run the following commands in your terminal to install and enable the plugin:
|
|
38
93
|
|
|
@@ -47,9 +102,9 @@ openclaw plugins enable openclaw-memori
|
|
|
47
102
|
openclaw gateway restart
|
|
48
103
|
```
|
|
49
104
|
|
|
50
|
-
|
|
105
|
+
### 2. Configure
|
|
51
106
|
|
|
52
|
-
The plugin needs your Memori API key and an Entity ID to function. You can configure this via the OpenClaw CLI
|
|
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.
|
|
53
108
|
|
|
54
109
|
### Option A: Via OpenClaw CLI (Recommended)
|
|
55
110
|
|
|
@@ -85,12 +140,39 @@ Add the following to your `~/.openclaw/openclaw.json` file:
|
|
|
85
140
|
| `apiKey` | `string` | **Yes** | Your Memori API key. |
|
|
86
141
|
| `entityId` | `string` | **Yes** | The unique identifier for the entity (e.g., user, agent, or tenant) to attribute these memories to. |
|
|
87
142
|
|
|
88
|
-
|
|
143
|
+
### 3. Verify
|
|
144
|
+
|
|
145
|
+
Restart the gateway and inspect the logs:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
openclaw gateway restart
|
|
149
|
+
openclaw gateway logs --filter "[Memori]"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
You should see:
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
[Memori] === INITIALIZING PLUGIN ===
|
|
156
|
+
[Memori] Tracking Entity ID: your-app-user-id
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
To test the full memory loop:
|
|
160
|
+
|
|
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.`
|
|
169
|
+
|
|
170
|
+
## How It Works
|
|
89
171
|
|
|
90
|
-
This plugin integrates
|
|
172
|
+
This plugin integrates with OpenClaw's event lifecycle to provide persistent memory without interfering with the agent's core logic:
|
|
91
173
|
|
|
92
|
-
1. **`before_prompt_build` (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.
|
|
93
|
-
2. **`agent_end` (
|
|
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.
|
|
94
176
|
|
|
95
177
|
## Contributing
|
|
96
178
|
|
package/dist/constants.d.ts
CHANGED
|
@@ -7,5 +7,16 @@ export declare const RECALL_CONFIG: {
|
|
|
7
7
|
readonly MIN_PROMPT_LENGTH: 2;
|
|
8
8
|
};
|
|
9
9
|
export declare const AUGMENTATION_CONFIG: {
|
|
10
|
-
readonly MAX_CONTEXT_MESSAGES:
|
|
10
|
+
readonly MAX_CONTEXT_MESSAGES: 20;
|
|
11
|
+
};
|
|
12
|
+
export declare const MESSAGE_CONSTANTS: {
|
|
13
|
+
readonly SILENT_REPLY: "SILENT_REPLY";
|
|
14
|
+
readonly NO_REPLY: "NO_REPLY";
|
|
15
|
+
readonly SYNTHETIC_RESPONSE: "Okay, I'll remember that for you.";
|
|
16
|
+
};
|
|
17
|
+
export declare const ROLE: {
|
|
18
|
+
readonly USER: "user";
|
|
19
|
+
readonly ASSISTANT: "assistant";
|
|
20
|
+
readonly TOOL_RESULT: "toolResult";
|
|
21
|
+
readonly SYSTEM: "system";
|
|
11
22
|
};
|
package/dist/constants.js
CHANGED
|
@@ -7,5 +7,16 @@ export const RECALL_CONFIG = {
|
|
|
7
7
|
MIN_PROMPT_LENGTH: 2,
|
|
8
8
|
};
|
|
9
9
|
export const AUGMENTATION_CONFIG = {
|
|
10
|
-
MAX_CONTEXT_MESSAGES:
|
|
10
|
+
MAX_CONTEXT_MESSAGES: 20,
|
|
11
|
+
};
|
|
12
|
+
export const MESSAGE_CONSTANTS = {
|
|
13
|
+
SILENT_REPLY: 'SILENT_REPLY',
|
|
14
|
+
NO_REPLY: 'NO_REPLY',
|
|
15
|
+
SYNTHETIC_RESPONSE: "Okay, I'll remember that for you.",
|
|
16
|
+
};
|
|
17
|
+
export const ROLE = {
|
|
18
|
+
USER: 'user',
|
|
19
|
+
ASSISTANT: 'assistant',
|
|
20
|
+
TOOL_RESULT: 'toolResult',
|
|
21
|
+
SYSTEM: 'system',
|
|
11
22
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { OpenClawEvent, OpenClawContext, MemoriPluginConfig } from '../types.js';
|
|
2
2
|
import { MemoriLogger } from '../utils/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Main handler for augmentation hook.
|
|
5
|
+
* Extracts the latest conversation turn and sends it to Memori backend.
|
|
6
|
+
*/
|
|
3
7
|
export declare function handleAugmentation(event: OpenClawEvent, ctx: OpenClawContext, config: MemoriPluginConfig, logger: MemoriLogger): Promise<void>;
|
|
@@ -1,78 +1,172 @@
|
|
|
1
1
|
import { extractContext, initializeMemoriClient } from '../utils/index.js';
|
|
2
2
|
import { cleanText, isSystemMessage } from '../sanitizer.js';
|
|
3
|
-
import { AUGMENTATION_CONFIG } from '../constants.js';
|
|
3
|
+
import { AUGMENTATION_CONFIG, MESSAGE_CONSTANTS, ROLE } from '../constants.js';
|
|
4
|
+
import { SDK_VERSION } from '../version.js';
|
|
5
|
+
/**
|
|
6
|
+
* Extracts metadata about the LLM provider and model used during the turn.
|
|
7
|
+
*/
|
|
4
8
|
function extractLLMMetadata(event) {
|
|
5
9
|
const messages = event.messages || [];
|
|
6
|
-
const lastAssistant = messages.findLast((m) => m.role ===
|
|
10
|
+
const lastAssistant = messages.findLast((m) => m.role === ROLE.ASSISTANT);
|
|
7
11
|
return {
|
|
8
12
|
provider: lastAssistant?.provider || null,
|
|
9
13
|
model: lastAssistant?.model || null,
|
|
10
14
|
sdkVersion: null,
|
|
11
|
-
|
|
12
|
-
integrationSdkVersion: '0.0.1', // TODO: move me back with first releases
|
|
15
|
+
integrationSdkVersion: SDK_VERSION,
|
|
13
16
|
platform: 'openclaw',
|
|
14
17
|
};
|
|
15
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Extracts all tool results from messages and maps them by tool call ID.
|
|
21
|
+
*/
|
|
22
|
+
function extractToolResults(messages) {
|
|
23
|
+
const resultsMap = new Map();
|
|
24
|
+
for (const msg of messages) {
|
|
25
|
+
if (msg.role === ROLE.TOOL_RESULT && msg.toolCallId) {
|
|
26
|
+
resultsMap.set(msg.toolCallId, cleanText(msg.content));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return resultsMap;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parses tool arguments from various formats into a structured object.
|
|
33
|
+
*/
|
|
34
|
+
function parseToolArguments(rawArgs) {
|
|
35
|
+
if (typeof rawArgs === 'string') {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(rawArgs);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (typeof rawArgs === 'object' && rawArgs !== null) {
|
|
44
|
+
return rawArgs;
|
|
45
|
+
}
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extracts tool calls from an assistant message.
|
|
50
|
+
* Iterates backward through content blocks to match original extraction order.
|
|
51
|
+
*/
|
|
52
|
+
function extractToolCalls(msg, toolResults) {
|
|
53
|
+
if (!Array.isArray(msg.content)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const tools = [];
|
|
57
|
+
for (let i = msg.content.length - 1; i >= 0; i--) {
|
|
58
|
+
const block = msg.content[i];
|
|
59
|
+
if (block.type === 'toolCall' || block.type === 'tool_use') {
|
|
60
|
+
const rawArgs = block.arguments ?? block.input;
|
|
61
|
+
// Unshift to maintain chronological order within the message
|
|
62
|
+
tools.unshift({
|
|
63
|
+
name: block.name,
|
|
64
|
+
args: parseToolArguments(rawArgs),
|
|
65
|
+
result: toolResults.get(block.id) ?? null,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return tools;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parses a user message, extracting the cleaned content.
|
|
73
|
+
*/
|
|
74
|
+
function parseUserMessage(msg) {
|
|
75
|
+
const cleanedContent = cleanText(msg.content);
|
|
76
|
+
return cleanedContent ? { role: msg.role, content: cleanedContent } : null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parses the most recent conversation turn from messages.
|
|
80
|
+
* Walks backward to find the last user message and assistant response.
|
|
81
|
+
* Collects all tool calls from assistant messages in the turn.
|
|
82
|
+
*/
|
|
83
|
+
function parseTurnFromMessages(messages) {
|
|
84
|
+
const tools = [];
|
|
85
|
+
const toolResults = extractToolResults(messages);
|
|
86
|
+
let userMessage = null;
|
|
87
|
+
let assistantMessage = null;
|
|
88
|
+
// Walk backwards to find the last complete turn
|
|
89
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
90
|
+
const msg = messages[i];
|
|
91
|
+
if (msg.role === ROLE.ASSISTANT) {
|
|
92
|
+
// Extract tool calls from ALL assistant messages in the turn
|
|
93
|
+
const extractedTools = extractToolCalls(msg, toolResults);
|
|
94
|
+
if (extractedTools.length > 0) {
|
|
95
|
+
// Prepend to maintain chronological order
|
|
96
|
+
tools.unshift(...extractedTools);
|
|
97
|
+
}
|
|
98
|
+
// Capture the text response from the FIRST (most recent) assistant message
|
|
99
|
+
if (!assistantMessage) {
|
|
100
|
+
const cleanedContent = cleanText(msg.content);
|
|
101
|
+
if (cleanedContent) {
|
|
102
|
+
assistantMessage = {
|
|
103
|
+
role: msg.role,
|
|
104
|
+
content: cleanedContent.replace(/^\[\[.*?\]\]\s*/, ''),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
else if (extractedTools.length > 0) {
|
|
108
|
+
assistantMessage = { role: msg.role, content: MESSAGE_CONSTANTS.SILENT_REPLY };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (msg.role === ROLE.USER) {
|
|
113
|
+
userMessage = parseUserMessage(msg);
|
|
114
|
+
break; // Found the user message that started this turn
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { userMessage, assistantMessage, tools };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Builds the augmentation payload to send to Memori backend.
|
|
121
|
+
*/
|
|
122
|
+
function buildAugmentationPayload(userMessage, agentResponse, tools, event) {
|
|
123
|
+
const payload = {
|
|
124
|
+
userMessage,
|
|
125
|
+
agentResponse,
|
|
126
|
+
metadata: extractLLMMetadata(event),
|
|
127
|
+
};
|
|
128
|
+
if (tools.length > 0) {
|
|
129
|
+
payload.trace = { tools };
|
|
130
|
+
}
|
|
131
|
+
return payload;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Helper to skip augmentation and log the reason.
|
|
135
|
+
*/
|
|
136
|
+
function skipAugmentation(logger, reason) {
|
|
137
|
+
logger.info(reason);
|
|
138
|
+
logger.endSection('AUGMENTATION HOOK END');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Main handler for augmentation hook.
|
|
142
|
+
* Extracts the latest conversation turn and sends it to Memori backend.
|
|
143
|
+
*/
|
|
16
144
|
export async function handleAugmentation(event, ctx, config, logger) {
|
|
17
145
|
logger.section('AUGMENTATION HOOK START');
|
|
18
146
|
if (!event.success || !event.messages || event.messages.length < 2) {
|
|
19
|
-
logger
|
|
20
|
-
logger.endSection('AUGMENTATION HOOK END');
|
|
147
|
+
skipAugmentation(logger, 'No messages or unsuccessful event. Skipping augmentation.');
|
|
21
148
|
return;
|
|
22
149
|
}
|
|
23
150
|
try {
|
|
24
151
|
const recentMessages = event.messages.slice(-AUGMENTATION_CONFIG.MAX_CONTEXT_MESSAGES);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const msg = recentMessages[i];
|
|
29
|
-
const role = msg.role;
|
|
30
|
-
if (role !== 'user' && role !== 'assistant')
|
|
31
|
-
continue;
|
|
32
|
-
const cleanedContent = cleanText(msg.content);
|
|
33
|
-
if (!cleanedContent)
|
|
34
|
-
continue;
|
|
35
|
-
let finalContent = cleanedContent;
|
|
36
|
-
if (role === 'assistant') {
|
|
37
|
-
finalContent = finalContent.replace(/^\[\[.*?\]\]\s*/, '');
|
|
38
|
-
}
|
|
39
|
-
if (role === 'assistant' && !lastAiMsg) {
|
|
40
|
-
lastAiMsg = { role, content: finalContent };
|
|
41
|
-
}
|
|
42
|
-
if (role === 'user' && !lastUserMsg) {
|
|
43
|
-
lastUserMsg = { role, content: finalContent };
|
|
44
|
-
}
|
|
45
|
-
if (lastUserMsg && lastAiMsg)
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
if (!lastUserMsg || !lastAiMsg) {
|
|
49
|
-
logger.info('Missing user or assistant message. Skipping.');
|
|
50
|
-
logger.endSection('AUGMENTATION HOOK END');
|
|
152
|
+
const turn = parseTurnFromMessages(recentMessages);
|
|
153
|
+
if (!turn.userMessage || !turn.assistantMessage) {
|
|
154
|
+
skipAugmentation(logger, 'Missing user or assistant message. Skipping.');
|
|
51
155
|
return;
|
|
52
156
|
}
|
|
53
|
-
if (isSystemMessage(
|
|
54
|
-
logger
|
|
55
|
-
logger.endSection('AUGMENTATION HOOK END');
|
|
157
|
+
if (isSystemMessage(turn.userMessage.content)) {
|
|
158
|
+
skipAugmentation(logger, 'User message is a system message. Skipping augmentation.');
|
|
56
159
|
return;
|
|
57
160
|
}
|
|
58
|
-
|
|
161
|
+
// Resolve synthetic responses for pure-tool executions
|
|
162
|
+
if (turn.assistantMessage.content === MESSAGE_CONSTANTS.SILENT_REPLY ||
|
|
163
|
+
turn.assistantMessage.content === MESSAGE_CONSTANTS.NO_REPLY) {
|
|
59
164
|
logger.info('Assistant used tool-based messaging. Using synthetic response.');
|
|
60
|
-
|
|
61
|
-
role: 'assistant',
|
|
62
|
-
content: "Okay, I'll remember that for you.",
|
|
63
|
-
};
|
|
165
|
+
turn.assistantMessage.content = MESSAGE_CONSTANTS.SYNTHETIC_RESPONSE;
|
|
64
166
|
}
|
|
167
|
+
const payload = buildAugmentationPayload(turn.userMessage.content, turn.assistantMessage.content, turn.tools, event);
|
|
65
168
|
const context = extractContext(event, ctx, config.entityId);
|
|
66
169
|
const memoriClient = initializeMemoriClient(config.apiKey, context);
|
|
67
|
-
logger.info('Capturing conversation turn...');
|
|
68
|
-
const payload = {
|
|
69
|
-
userMessage: lastUserMsg.content,
|
|
70
|
-
agentResponse: lastAiMsg.content,
|
|
71
|
-
metadata: extractLLMMetadata(event),
|
|
72
|
-
};
|
|
73
|
-
logger.info(`Sending User: ${payload.userMessage}`);
|
|
74
|
-
logger.info(`Sending Agent: ${payload.agentResponse}`);
|
|
75
|
-
logger.info(`Sending Meta: ${JSON.stringify(payload.metadata)}`);
|
|
76
170
|
await memoriClient.augmentation(payload);
|
|
77
171
|
logger.info('Augmentation successful!');
|
|
78
172
|
}
|
package/dist/handlers/recall.js
CHANGED
|
@@ -5,7 +5,6 @@ export async function handleRecall(event, ctx, config, logger) {
|
|
|
5
5
|
logger.section('RECALL HOOK START');
|
|
6
6
|
try {
|
|
7
7
|
const context = extractContext(event, ctx, config.entityId);
|
|
8
|
-
logger.info(`EntityID: ${context.entityId} | SessionID: ${context.sessionId} | Provider: ${context.provider}`);
|
|
9
8
|
const promptText = cleanText(event.prompt);
|
|
10
9
|
if (!promptText ||
|
|
11
10
|
promptText.length < RECALL_CONFIG.MIN_PROMPT_LENGTH ||
|
|
@@ -23,7 +22,6 @@ export async function handleRecall(event, ctx, config, logger) {
|
|
|
23
22
|
else {
|
|
24
23
|
logger.info('No relevant memories found.');
|
|
25
24
|
}
|
|
26
|
-
logger.info(`Recall Prompt: ${hookReturn?.prependContext}`);
|
|
27
25
|
return hookReturn;
|
|
28
26
|
}
|
|
29
27
|
catch (err) {
|
package/dist/types.d.ts
CHANGED
|
@@ -6,12 +6,18 @@ export interface OpenClawMessageBlock {
|
|
|
6
6
|
type?: string;
|
|
7
7
|
text?: string;
|
|
8
8
|
thinking?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
arguments?: unknown;
|
|
9
12
|
[key: string]: unknown;
|
|
10
13
|
}
|
|
11
14
|
export interface OpenClawMessage {
|
|
12
|
-
role:
|
|
15
|
+
role: string;
|
|
13
16
|
content: string | OpenClawMessageBlock[];
|
|
14
17
|
timestamp?: number;
|
|
18
|
+
toolCallId?: string;
|
|
19
|
+
provider?: string;
|
|
20
|
+
model?: string;
|
|
15
21
|
[key: string]: unknown;
|
|
16
22
|
}
|
|
17
23
|
export interface OpenClawEvent {
|
|
@@ -32,3 +38,19 @@ export interface OpenClawContext {
|
|
|
32
38
|
workspaceDir?: string;
|
|
33
39
|
messageProvider?: string;
|
|
34
40
|
}
|
|
41
|
+
export interface ExtractedToolCall {
|
|
42
|
+
name: string;
|
|
43
|
+
args: Record<string, unknown>;
|
|
44
|
+
result: unknown;
|
|
45
|
+
}
|
|
46
|
+
export interface ParsedTurn {
|
|
47
|
+
userMessage: {
|
|
48
|
+
role: string;
|
|
49
|
+
content: string;
|
|
50
|
+
} | null;
|
|
51
|
+
assistantMessage: {
|
|
52
|
+
role: string;
|
|
53
|
+
content: string;
|
|
54
|
+
} | null;
|
|
55
|
+
tools: ExtractedToolCall[];
|
|
56
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.0.
|
|
1
|
+
export declare const SDK_VERSION = "0.0.6";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = '0.0.
|
|
1
|
+
export const SDK_VERSION = '0.0.6';
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memorilabs/openclaw-memori",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Official MemoriLabs.ai long-term memory plugin for OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -62,7 +62,10 @@
|
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=22.0.0"
|
|
64
64
|
},
|
|
65
|
+
"overrides": {
|
|
66
|
+
"@hono/node-server": "^1.19.10"
|
|
67
|
+
},
|
|
65
68
|
"dependencies": {
|
|
66
|
-
"@memorilabs/memori": "^0.0.
|
|
69
|
+
"@memorilabs/memori": "^0.0.8"
|
|
67
70
|
}
|
|
68
71
|
}
|