@memorilabs/openclaw-memori 0.0.5 → 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/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 +141 -43
- package/dist/types.d.ts +23 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
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,10 +1,13 @@
|
|
|
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
4
|
import { SDK_VERSION } from '../version.js';
|
|
5
|
+
/**
|
|
6
|
+
* Extracts metadata about the LLM provider and model used during the turn.
|
|
7
|
+
*/
|
|
5
8
|
function extractLLMMetadata(event) {
|
|
6
9
|
const messages = event.messages || [];
|
|
7
|
-
const lastAssistant = messages.findLast((m) => m.role ===
|
|
10
|
+
const lastAssistant = messages.findLast((m) => m.role === ROLE.ASSISTANT);
|
|
8
11
|
return {
|
|
9
12
|
provider: lastAssistant?.provider || null,
|
|
10
13
|
model: lastAssistant?.model || null,
|
|
@@ -13,62 +16,157 @@ function extractLLMMetadata(event) {
|
|
|
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
|
-
const payload = {
|
|
68
|
-
userMessage: lastUserMsg.content,
|
|
69
|
-
agentResponse: lastAiMsg.content,
|
|
70
|
-
metadata: extractLLMMetadata(event),
|
|
71
|
-
};
|
|
72
170
|
await memoriClient.augmentation(payload);
|
|
73
171
|
logger.info('Augmentation successful!');
|
|
74
172
|
}
|
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/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",
|
|
@@ -66,6 +66,6 @@
|
|
|
66
66
|
"@hono/node-server": "^1.19.10"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@memorilabs/memori": "^0.0.
|
|
69
|
+
"@memorilabs/memori": "^0.0.8"
|
|
70
70
|
}
|
|
71
71
|
}
|