@bamdra/bamdra-openclaw-memory 0.3.8 → 0.3.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.
package/dist/index.js
CHANGED
|
@@ -107,6 +107,7 @@ var FactExtractor = class {
|
|
|
107
107
|
candidates.push(...extractAccountLikeFacts(input));
|
|
108
108
|
candidates.push(...extractConstraintFacts(input));
|
|
109
109
|
candidates.push(...extractPreferenceFacts(input));
|
|
110
|
+
candidates.push(...extractPersonalWorkFacts(input));
|
|
110
111
|
return dedupeCandidates(candidates);
|
|
111
112
|
}
|
|
112
113
|
};
|
|
@@ -134,7 +135,7 @@ function extractAccountLikeFacts(input) {
|
|
|
134
135
|
if (!/(账号|账户|account|appid|appsecret|token|apikey|api key)/i.test(input.text)) {
|
|
135
136
|
return candidates;
|
|
136
137
|
}
|
|
137
|
-
const scope = input.topic ? `topic:${input.topic.id}` :
|
|
138
|
+
const scope = input.topic ? `topic:${input.topic.id}` : fallbackSharedScope(input);
|
|
138
139
|
const labels = input.topic?.labels ?? [];
|
|
139
140
|
if (/(appid|appsecret|token|apikey|api key)/i.test(input.text)) {
|
|
140
141
|
candidates.push({
|
|
@@ -173,7 +174,7 @@ function extractConstraintFacts(input) {
|
|
|
173
174
|
value: abbreviate(input.text),
|
|
174
175
|
sensitivity: "normal",
|
|
175
176
|
recallPolicy: "topic_bound",
|
|
176
|
-
scope: input.topic ? `topic:${input.topic.id}` :
|
|
177
|
+
scope: input.topic ? `topic:${input.topic.id}` : fallbackSharedScope(input),
|
|
177
178
|
confidence: 0.82,
|
|
178
179
|
tags: [...input.topic?.labels ?? [], "constraint"]
|
|
179
180
|
}
|
|
@@ -190,12 +191,49 @@ function extractPreferenceFacts(input) {
|
|
|
190
191
|
value: abbreviate(input.text),
|
|
191
192
|
sensitivity: "normal",
|
|
192
193
|
recallPolicy: "always",
|
|
193
|
-
scope:
|
|
194
|
+
scope: choosePersonalOrSharedScope(input),
|
|
194
195
|
confidence: 0.76,
|
|
195
196
|
tags: [...input.topic?.labels ?? [], "preference"]
|
|
196
197
|
}
|
|
197
198
|
];
|
|
198
199
|
}
|
|
200
|
+
function extractPersonalWorkFacts(input) {
|
|
201
|
+
const explicitCurrentWork = input.text.match(/(?:记住[::]?\s*)?我(?:现在)?主要在做(.+?)[。.!!\n]?$/) ?? input.text.match(/(?:记住[::]?\s*)?我最近主要在做(.+?)[。.!!\n]?$/) ?? input.text.match(/(?:记住[::]?\s*)?我当前主要在做(.+?)[。.!!\n]?$/);
|
|
202
|
+
if (!explicitCurrentWork) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
const value = explicitCurrentWork[1]?.trim();
|
|
206
|
+
if (!value) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
return [
|
|
210
|
+
{
|
|
211
|
+
category: "project",
|
|
212
|
+
key: "\u5F53\u524D\u4E3B\u8981\u5DE5\u4F5C",
|
|
213
|
+
value,
|
|
214
|
+
sensitivity: "normal",
|
|
215
|
+
recallPolicy: "always",
|
|
216
|
+
scope: choosePersonalOrSharedScope(input),
|
|
217
|
+
confidence: 0.92,
|
|
218
|
+
tags: [...input.topic?.labels ?? [], "project", "current-focus"]
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
function choosePersonalOrSharedScope(input) {
|
|
223
|
+
if (shouldForceShared(input.text)) {
|
|
224
|
+
return "shared";
|
|
225
|
+
}
|
|
226
|
+
if (input.userId) {
|
|
227
|
+
return `user:${input.userId}`;
|
|
228
|
+
}
|
|
229
|
+
return fallbackSharedScope(input);
|
|
230
|
+
}
|
|
231
|
+
function fallbackSharedScope(input) {
|
|
232
|
+
return input.topic ? `topic:${input.topic.id}` : "shared";
|
|
233
|
+
}
|
|
234
|
+
function shouldForceShared(text) {
|
|
235
|
+
return /(共享|shared|公共|团队|所有人|everyone|team|public)/i.test(text);
|
|
236
|
+
}
|
|
199
237
|
function dedupeCandidates(candidates) {
|
|
200
238
|
const seen = /* @__PURE__ */ new Set();
|
|
201
239
|
return candidates.filter((candidate) => {
|
|
@@ -363,6 +401,32 @@ var MemorySqliteStore = class {
|
|
|
363
401
|
record.rawJson
|
|
364
402
|
);
|
|
365
403
|
}
|
|
404
|
+
async backfillSessionIdentity(args) {
|
|
405
|
+
this.db.exec("BEGIN");
|
|
406
|
+
try {
|
|
407
|
+
this.db.prepare(
|
|
408
|
+
`UPDATE ${TABLES.messages}
|
|
409
|
+
SET user_id = COALESCE(user_id, ?),
|
|
410
|
+
channel_type = COALESCE(channel_type, ?),
|
|
411
|
+
sender_open_id = COALESCE(sender_open_id, ?)
|
|
412
|
+
WHERE session_id = ?`
|
|
413
|
+
).run(args.userId, args.channelType ?? null, args.senderOpenId ?? null, args.sessionId);
|
|
414
|
+
this.db.prepare(
|
|
415
|
+
`UPDATE ${TABLES.topics}
|
|
416
|
+
SET user_id = COALESCE(user_id, ?)
|
|
417
|
+
WHERE session_id = ?`
|
|
418
|
+
).run(args.userId, args.sessionId);
|
|
419
|
+
this.db.prepare(
|
|
420
|
+
`UPDATE ${TABLES.sessionState}
|
|
421
|
+
SET user_id = COALESCE(user_id, ?)
|
|
422
|
+
WHERE session_id = ?`
|
|
423
|
+
).run(args.userId, args.sessionId);
|
|
424
|
+
this.db.exec("COMMIT");
|
|
425
|
+
} catch (error) {
|
|
426
|
+
this.db.exec("ROLLBACK");
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
366
430
|
async getSessionState(sessionId) {
|
|
367
431
|
const row = this.db.prepare(
|
|
368
432
|
`SELECT session_id, user_id, active_topic_id, last_compacted_at, last_turn_id, updated_at
|
|
@@ -1241,6 +1305,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1241
1305
|
async (event) => {
|
|
1242
1306
|
await ensureResolvedIdentity(event);
|
|
1243
1307
|
const sessionId = getSessionIdFromHookContext(event);
|
|
1308
|
+
await backfillResolvedIdentity(store, sessionId);
|
|
1244
1309
|
const text = getTextFromHookContext(event);
|
|
1245
1310
|
logMemoryEvent("hook-ingest-received", {
|
|
1246
1311
|
hasSessionId: Boolean(sessionId),
|
|
@@ -1270,6 +1335,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1270
1335
|
registerTypedHook("before_prompt_build", async (event, hookContext) => {
|
|
1271
1336
|
await ensureResolvedIdentity(hookContext);
|
|
1272
1337
|
const sessionId = getSessionIdFromHookContext(hookContext);
|
|
1338
|
+
await backfillResolvedIdentity(store, sessionId);
|
|
1273
1339
|
const text = getTextFromHookContext(event);
|
|
1274
1340
|
logMemoryEvent("hook-assemble-received", {
|
|
1275
1341
|
hasSessionId: Boolean(sessionId),
|
|
@@ -1566,6 +1632,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1566
1632
|
await store.upsertTopicMembership(membership);
|
|
1567
1633
|
const extractedFacts = factExtractor.extract({
|
|
1568
1634
|
sessionId,
|
|
1635
|
+
userId,
|
|
1569
1636
|
text,
|
|
1570
1637
|
topic: decision.action === "spawn" ? topicRecord : {
|
|
1571
1638
|
...topicRecord,
|
|
@@ -1682,6 +1749,21 @@ async function ensureResolvedIdentity(context) {
|
|
|
1682
1749
|
});
|
|
1683
1750
|
}
|
|
1684
1751
|
}
|
|
1752
|
+
async function backfillResolvedIdentity(store, sessionId) {
|
|
1753
|
+
if (!sessionId || typeof store.backfillSessionIdentity !== "function") {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
const userId = getResolvedUserId(sessionId);
|
|
1757
|
+
if (!userId) {
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
await store.backfillSessionIdentity({
|
|
1761
|
+
sessionId,
|
|
1762
|
+
userId,
|
|
1763
|
+
channelType: getResolvedChannelType(sessionId),
|
|
1764
|
+
senderOpenId: getResolvedSenderOpenId(sessionId)
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1685
1767
|
function getResolvedUserId(sessionId) {
|
|
1686
1768
|
const identity = getResolvedIdentity(sessionId);
|
|
1687
1769
|
const userId = identity?.userId;
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamdra/bamdra-openclaw-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Unified topic-aware memory plugin for OpenClaw with durable SQLite recall and bounded context growth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.bamdra.com",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@bamdra/bamdra-user-bind": "^0.1.
|
|
63
|
-
"@bamdra/bamdra-memory-vector": "^0.1.
|
|
62
|
+
"@bamdra/bamdra-user-bind": "^0.1.7",
|
|
63
|
+
"@bamdra/bamdra-memory-vector": "^0.1.7"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/node": "^24.5.2"
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamdra/bamdra-openclaw-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Unified topic-aware memory plugin for OpenClaw with durable SQLite recall and bounded context growth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.bamdra.com",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@bamdra/bamdra-user-bind": "^0.1.
|
|
63
|
-
"@bamdra/bamdra-memory-vector": "^0.1.
|
|
62
|
+
"@bamdra/bamdra-user-bind": "^0.1.7",
|
|
63
|
+
"@bamdra/bamdra-memory-vector": "^0.1.7"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/node": "^24.5.2"
|
|
@@ -107,6 +107,28 @@ If `bamdra-memory-vector` is enabled, treat `bamdra_memory_search` as a hybrid r
|
|
|
107
107
|
- If memory retrieval feels privacy-sensitive or contextually risky, narrow the search or answer conservatively.
|
|
108
108
|
- Treat `bamdra-user-bind` profile data as private-by-default user context, not as a global team directory.
|
|
109
109
|
|
|
110
|
+
## Scope Rules
|
|
111
|
+
|
|
112
|
+
Use `shared` only for genuinely reusable public knowledge.
|
|
113
|
+
|
|
114
|
+
Good `shared` examples:
|
|
115
|
+
|
|
116
|
+
- team conventions
|
|
117
|
+
- reusable SOPs
|
|
118
|
+
- public project templates
|
|
119
|
+
- shared knowledge base entries
|
|
120
|
+
- information the user explicitly says should be shared
|
|
121
|
+
|
|
122
|
+
Keep these private by default and prefer `user:` scope or `bamdra-user-bind` profile updates:
|
|
123
|
+
|
|
124
|
+
- preferred form of address
|
|
125
|
+
- timezone
|
|
126
|
+
- tone and formatting preferences
|
|
127
|
+
- pets, family, role, and personal background
|
|
128
|
+
- the user's current focus, ongoing work, and long-lived personal goals
|
|
129
|
+
|
|
130
|
+
When a fact is about “me”, “my”, or the current user's own stable way of working, do not store it as `shared` unless the user explicitly says it is a shared rule for everyone.
|
|
131
|
+
|
|
110
132
|
## Working Style
|
|
111
133
|
|
|
112
134
|
- Keep memory behavior mostly invisible.
|