@bamdra/bamdra-openclaw-memory 0.3.0 → 0.3.2
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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +508 -59
- package/dist/index.js.map +1 -1
- package/dist/openclaw.plugin.json +1 -1
- package/dist/package.json +8 -5
- package/dist/schema.sql +36 -22
- package/openclaw.plugin.json +1 -1
- package/package.json +12 -10
- package/skills/bamdra-memory-operator/SKILL.md +5 -5
package/dist/index.js
CHANGED
|
@@ -218,9 +218,9 @@ function stableKeyFragment(text) {
|
|
|
218
218
|
// ../../packages/memory-cache-memory/src/index.ts
|
|
219
219
|
function logCacheEvent(event, details = {}) {
|
|
220
220
|
try {
|
|
221
|
-
console.info("[bamdra-memory-cache]", event, JSON.stringify(details));
|
|
221
|
+
console.info("[bamdra-openclaw-memory-cache]", event, JSON.stringify(details));
|
|
222
222
|
} catch {
|
|
223
|
-
console.info("[bamdra-memory-cache]", event);
|
|
223
|
+
console.info("[bamdra-openclaw-memory-cache]", event);
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
var InMemoryCacheStore = class {
|
|
@@ -291,7 +291,17 @@ var import_promises = require("node:fs/promises");
|
|
|
291
291
|
var import_node_path = require("node:path");
|
|
292
292
|
var import_node_sqlite = require("node:sqlite");
|
|
293
293
|
var import_node_url = require("node:url");
|
|
294
|
-
var SCHEMA_VERSION =
|
|
294
|
+
var SCHEMA_VERSION = 2;
|
|
295
|
+
var TABLES = {
|
|
296
|
+
schemaMigrations: "bamdra_memory_schema_migrations",
|
|
297
|
+
messages: "bamdra_memory_messages",
|
|
298
|
+
topics: "bamdra_memory_topics",
|
|
299
|
+
topicMembership: "bamdra_memory_topic_membership",
|
|
300
|
+
facts: "bamdra_memory_facts",
|
|
301
|
+
factTags: "bamdra_memory_fact_tags",
|
|
302
|
+
contextSnapshots: "bamdra_memory_context_snapshots",
|
|
303
|
+
sessionState: "bamdra_memory_session_state"
|
|
304
|
+
};
|
|
295
305
|
var MemorySqliteStore = class {
|
|
296
306
|
constructor(options) {
|
|
297
307
|
this.options = options;
|
|
@@ -305,15 +315,19 @@ var MemorySqliteStore = class {
|
|
|
305
315
|
async applyMigrations() {
|
|
306
316
|
const schemaSql = await loadSchemaSql();
|
|
307
317
|
this.db.exec(schemaSql);
|
|
318
|
+
applyUserIdentityMigrations(this.db);
|
|
308
319
|
}
|
|
309
320
|
async close() {
|
|
310
321
|
this.db.close();
|
|
311
322
|
}
|
|
312
323
|
async upsertMessage(record) {
|
|
313
324
|
this.db.prepare(
|
|
314
|
-
`INSERT INTO messages (
|
|
325
|
+
`INSERT INTO ${TABLES.messages} (
|
|
315
326
|
id,
|
|
316
327
|
session_id,
|
|
328
|
+
user_id,
|
|
329
|
+
channel_type,
|
|
330
|
+
sender_open_id,
|
|
317
331
|
turn_id,
|
|
318
332
|
parent_turn_id,
|
|
319
333
|
role,
|
|
@@ -321,9 +335,12 @@ var MemorySqliteStore = class {
|
|
|
321
335
|
text,
|
|
322
336
|
ts,
|
|
323
337
|
raw_json
|
|
324
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
338
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
325
339
|
ON CONFLICT(id) DO UPDATE SET
|
|
326
340
|
session_id = excluded.session_id,
|
|
341
|
+
user_id = excluded.user_id,
|
|
342
|
+
channel_type = excluded.channel_type,
|
|
343
|
+
sender_open_id = excluded.sender_open_id,
|
|
327
344
|
turn_id = excluded.turn_id,
|
|
328
345
|
parent_turn_id = excluded.parent_turn_id,
|
|
329
346
|
role = excluded.role,
|
|
@@ -334,6 +351,9 @@ var MemorySqliteStore = class {
|
|
|
334
351
|
).run(
|
|
335
352
|
record.id,
|
|
336
353
|
record.sessionId,
|
|
354
|
+
record.userId,
|
|
355
|
+
record.channelType,
|
|
356
|
+
record.senderOpenId,
|
|
337
357
|
record.turnId,
|
|
338
358
|
record.parentTurnId,
|
|
339
359
|
record.role,
|
|
@@ -345,28 +365,31 @@ var MemorySqliteStore = class {
|
|
|
345
365
|
}
|
|
346
366
|
async getSessionState(sessionId) {
|
|
347
367
|
const row = this.db.prepare(
|
|
348
|
-
`SELECT session_id, active_topic_id, last_compacted_at, last_turn_id, updated_at
|
|
349
|
-
FROM
|
|
368
|
+
`SELECT session_id, user_id, active_topic_id, last_compacted_at, last_turn_id, updated_at
|
|
369
|
+
FROM ${TABLES.sessionState}
|
|
350
370
|
WHERE session_id = ?`
|
|
351
371
|
).get(sessionId);
|
|
352
372
|
return row ? mapSessionStateRow(row) : null;
|
|
353
373
|
}
|
|
354
374
|
async upsertSessionState(record) {
|
|
355
375
|
this.db.prepare(
|
|
356
|
-
`INSERT INTO
|
|
376
|
+
`INSERT INTO ${TABLES.sessionState} (
|
|
357
377
|
session_id,
|
|
378
|
+
user_id,
|
|
358
379
|
active_topic_id,
|
|
359
380
|
last_compacted_at,
|
|
360
381
|
last_turn_id,
|
|
361
382
|
updated_at
|
|
362
|
-
) VALUES (?, ?, ?, ?, ?)
|
|
383
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
363
384
|
ON CONFLICT(session_id) DO UPDATE SET
|
|
385
|
+
user_id = excluded.user_id,
|
|
364
386
|
active_topic_id = excluded.active_topic_id,
|
|
365
387
|
last_compacted_at = excluded.last_compacted_at,
|
|
366
388
|
last_turn_id = excluded.last_turn_id,
|
|
367
389
|
updated_at = excluded.updated_at`
|
|
368
390
|
).run(
|
|
369
391
|
record.sessionId,
|
|
392
|
+
record.userId,
|
|
370
393
|
record.activeTopicId,
|
|
371
394
|
record.lastCompactedAt,
|
|
372
395
|
record.lastTurnId,
|
|
@@ -378,6 +401,7 @@ var MemorySqliteStore = class {
|
|
|
378
401
|
`SELECT
|
|
379
402
|
id,
|
|
380
403
|
session_id,
|
|
404
|
+
user_id,
|
|
381
405
|
title,
|
|
382
406
|
status,
|
|
383
407
|
parent_topic_id,
|
|
@@ -387,7 +411,7 @@ var MemorySqliteStore = class {
|
|
|
387
411
|
labels_json,
|
|
388
412
|
created_at,
|
|
389
413
|
last_active_at
|
|
390
|
-
FROM topics
|
|
414
|
+
FROM ${TABLES.topics}
|
|
391
415
|
WHERE session_id = ?
|
|
392
416
|
ORDER BY last_active_at DESC`
|
|
393
417
|
).all(sessionId);
|
|
@@ -398,6 +422,7 @@ var MemorySqliteStore = class {
|
|
|
398
422
|
`SELECT
|
|
399
423
|
id,
|
|
400
424
|
session_id,
|
|
425
|
+
user_id,
|
|
401
426
|
title,
|
|
402
427
|
status,
|
|
403
428
|
parent_topic_id,
|
|
@@ -407,16 +432,17 @@ var MemorySqliteStore = class {
|
|
|
407
432
|
labels_json,
|
|
408
433
|
created_at,
|
|
409
434
|
last_active_at
|
|
410
|
-
FROM topics
|
|
435
|
+
FROM ${TABLES.topics}
|
|
411
436
|
WHERE id = ?`
|
|
412
437
|
).get(topicId);
|
|
413
438
|
return row ? mapTopicRow(row) : null;
|
|
414
439
|
}
|
|
415
440
|
async upsertTopic(record) {
|
|
416
441
|
this.db.prepare(
|
|
417
|
-
`INSERT INTO topics (
|
|
442
|
+
`INSERT INTO ${TABLES.topics} (
|
|
418
443
|
id,
|
|
419
444
|
session_id,
|
|
445
|
+
user_id,
|
|
420
446
|
title,
|
|
421
447
|
status,
|
|
422
448
|
parent_topic_id,
|
|
@@ -426,9 +452,10 @@ var MemorySqliteStore = class {
|
|
|
426
452
|
labels_json,
|
|
427
453
|
created_at,
|
|
428
454
|
last_active_at
|
|
429
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
455
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
430
456
|
ON CONFLICT(id) DO UPDATE SET
|
|
431
457
|
session_id = excluded.session_id,
|
|
458
|
+
user_id = excluded.user_id,
|
|
432
459
|
title = excluded.title,
|
|
433
460
|
status = excluded.status,
|
|
434
461
|
parent_topic_id = excluded.parent_topic_id,
|
|
@@ -441,6 +468,7 @@ var MemorySqliteStore = class {
|
|
|
441
468
|
).run(
|
|
442
469
|
record.id,
|
|
443
470
|
record.sessionId,
|
|
471
|
+
record.userId,
|
|
444
472
|
record.title,
|
|
445
473
|
record.status,
|
|
446
474
|
record.parentTopicId,
|
|
@@ -454,7 +482,7 @@ var MemorySqliteStore = class {
|
|
|
454
482
|
}
|
|
455
483
|
async upsertTopicMembership(record) {
|
|
456
484
|
this.db.prepare(
|
|
457
|
-
`INSERT INTO
|
|
485
|
+
`INSERT INTO ${TABLES.topicMembership} (
|
|
458
486
|
message_id,
|
|
459
487
|
topic_id,
|
|
460
488
|
score,
|
|
@@ -480,7 +508,7 @@ var MemorySqliteStore = class {
|
|
|
480
508
|
this.db.exec("BEGIN");
|
|
481
509
|
try {
|
|
482
510
|
this.db.prepare(
|
|
483
|
-
`INSERT INTO facts (
|
|
511
|
+
`INSERT INTO ${TABLES.facts} (
|
|
484
512
|
id,
|
|
485
513
|
scope,
|
|
486
514
|
category,
|
|
@@ -517,9 +545,9 @@ var MemorySqliteStore = class {
|
|
|
517
545
|
record.sourceTopicId,
|
|
518
546
|
record.updatedAt
|
|
519
547
|
);
|
|
520
|
-
this.db.prepare(`DELETE FROM
|
|
548
|
+
this.db.prepare(`DELETE FROM ${TABLES.factTags} WHERE fact_id = ?`).run(record.id);
|
|
521
549
|
const insertTag = this.db.prepare(
|
|
522
|
-
`INSERT INTO
|
|
550
|
+
`INSERT INTO ${TABLES.factTags} (fact_id, tag) VALUES (?, ?)`
|
|
523
551
|
);
|
|
524
552
|
for (const tag of tags) {
|
|
525
553
|
insertTag.run(record.id, tag);
|
|
@@ -545,8 +573,8 @@ var MemorySqliteStore = class {
|
|
|
545
573
|
f.source_topic_id,
|
|
546
574
|
f.updated_at,
|
|
547
575
|
COALESCE(json_group_array(ft.tag) FILTER (WHERE ft.tag IS NOT NULL), '[]') AS tags_json
|
|
548
|
-
FROM facts f
|
|
549
|
-
LEFT JOIN
|
|
576
|
+
FROM ${TABLES.facts} f
|
|
577
|
+
LEFT JOIN ${TABLES.factTags} ft ON ft.fact_id = f.id
|
|
550
578
|
WHERE f.scope = ?
|
|
551
579
|
GROUP BY
|
|
552
580
|
f.id,
|
|
@@ -584,9 +612,9 @@ var MemorySqliteStore = class {
|
|
|
584
612
|
f.source_topic_id,
|
|
585
613
|
f.updated_at,
|
|
586
614
|
COALESCE(json_group_array(DISTINCT ft_all.tag) FILTER (WHERE ft_all.tag IS NOT NULL), '[]') AS tags_json
|
|
587
|
-
FROM facts f
|
|
588
|
-
JOIN
|
|
589
|
-
LEFT JOIN
|
|
615
|
+
FROM ${TABLES.facts} f
|
|
616
|
+
JOIN ${TABLES.factTags} ft_match ON ft_match.fact_id = f.id
|
|
617
|
+
LEFT JOIN ${TABLES.factTags} ft_all ON ft_all.fact_id = f.id
|
|
590
618
|
WHERE ft_match.tag IN (${tagPlaceholders})
|
|
591
619
|
AND f.recall_policy IN (${recallPolicyPlaceholders})
|
|
592
620
|
GROUP BY
|
|
@@ -616,6 +644,9 @@ var MemorySqliteStore = class {
|
|
|
616
644
|
tm.created_at,
|
|
617
645
|
m.id,
|
|
618
646
|
m.session_id,
|
|
647
|
+
m.user_id,
|
|
648
|
+
m.channel_type,
|
|
649
|
+
m.sender_open_id,
|
|
619
650
|
m.turn_id,
|
|
620
651
|
m.parent_turn_id,
|
|
621
652
|
m.role,
|
|
@@ -623,8 +654,8 @@ var MemorySqliteStore = class {
|
|
|
623
654
|
m.text,
|
|
624
655
|
m.ts,
|
|
625
656
|
m.raw_json
|
|
626
|
-
FROM
|
|
627
|
-
JOIN messages m ON m.id = tm.message_id
|
|
657
|
+
FROM ${TABLES.topicMembership} tm
|
|
658
|
+
JOIN ${TABLES.messages} m ON m.id = tm.message_id
|
|
628
659
|
WHERE tm.topic_id = ?
|
|
629
660
|
ORDER BY m.ts DESC
|
|
630
661
|
LIMIT ?`
|
|
@@ -650,7 +681,7 @@ var MemorySqliteStore = class {
|
|
|
650
681
|
labels_json,
|
|
651
682
|
created_at,
|
|
652
683
|
last_active_at
|
|
653
|
-
FROM topics
|
|
684
|
+
FROM ${TABLES.topics}
|
|
654
685
|
WHERE session_id = ?
|
|
655
686
|
AND (
|
|
656
687
|
lower(title) LIKE ? ESCAPE '\\'
|
|
@@ -671,6 +702,7 @@ var MemorySqliteStore = class {
|
|
|
671
702
|
const likeValue = `%${escapeLike(normalizedQuery)}%`;
|
|
672
703
|
const topicScope = args.topicId ? `topic:${args.topicId}` : null;
|
|
673
704
|
const sessionScope = `session:${args.sessionId}`;
|
|
705
|
+
const userScope = args.userId ? `user:${args.userId}` : null;
|
|
674
706
|
const rows = this.db.prepare(
|
|
675
707
|
`SELECT
|
|
676
708
|
f.id,
|
|
@@ -685,15 +717,16 @@ var MemorySqliteStore = class {
|
|
|
685
717
|
f.source_topic_id,
|
|
686
718
|
f.updated_at,
|
|
687
719
|
COALESCE(json_group_array(DISTINCT ft_all.tag) FILTER (WHERE ft_all.tag IS NOT NULL), '[]') AS tags_json
|
|
688
|
-
FROM facts f
|
|
689
|
-
LEFT JOIN
|
|
690
|
-
LEFT JOIN
|
|
720
|
+
FROM ${TABLES.facts} f
|
|
721
|
+
LEFT JOIN ${TABLES.factTags} ft_match ON ft_match.fact_id = f.id
|
|
722
|
+
LEFT JOIN ${TABLES.factTags} ft_all ON ft_all.fact_id = f.id
|
|
691
723
|
WHERE (
|
|
692
724
|
f.scope IN ('global', 'shared')
|
|
693
725
|
OR f.scope = ?
|
|
694
726
|
OR f.scope = ?
|
|
727
|
+
OR f.scope = ?
|
|
695
728
|
OR f.source_topic_id IN (
|
|
696
|
-
SELECT id FROM topics WHERE session_id = ?
|
|
729
|
+
SELECT id FROM ${TABLES.topics} WHERE session_id = ? AND (? IS NULL OR user_id = ?)
|
|
697
730
|
)
|
|
698
731
|
)
|
|
699
732
|
AND (
|
|
@@ -716,7 +749,19 @@ var MemorySqliteStore = class {
|
|
|
716
749
|
f.updated_at
|
|
717
750
|
ORDER BY f.updated_at DESC
|
|
718
751
|
LIMIT ?`
|
|
719
|
-
).all(
|
|
752
|
+
).all(
|
|
753
|
+
sessionScope,
|
|
754
|
+
topicScope,
|
|
755
|
+
userScope,
|
|
756
|
+
args.sessionId,
|
|
757
|
+
args.userId ?? null,
|
|
758
|
+
args.userId ?? null,
|
|
759
|
+
likeValue,
|
|
760
|
+
likeValue,
|
|
761
|
+
likeValue,
|
|
762
|
+
likeValue,
|
|
763
|
+
args.limit
|
|
764
|
+
);
|
|
720
765
|
return rows.map((row) => mapFactSearchResult(row, normalizedQuery)).sort((a, b) => b.score - a.score);
|
|
721
766
|
}
|
|
722
767
|
};
|
|
@@ -738,6 +783,7 @@ async function loadSchemaSql() {
|
|
|
738
783
|
function mapSessionStateRow(row) {
|
|
739
784
|
return {
|
|
740
785
|
sessionId: row.session_id,
|
|
786
|
+
userId: row.user_id,
|
|
741
787
|
activeTopicId: row.active_topic_id,
|
|
742
788
|
lastCompactedAt: row.last_compacted_at,
|
|
743
789
|
lastTurnId: row.last_turn_id,
|
|
@@ -748,6 +794,7 @@ function mapTopicRow(row) {
|
|
|
748
794
|
return {
|
|
749
795
|
id: row.id,
|
|
750
796
|
sessionId: row.session_id,
|
|
797
|
+
userId: row.user_id,
|
|
751
798
|
title: row.title,
|
|
752
799
|
status: row.status,
|
|
753
800
|
parentTopicId: row.parent_topic_id,
|
|
@@ -787,7 +834,8 @@ function mapTopicSearchResult(row, normalizedQuery) {
|
|
|
787
834
|
return {
|
|
788
835
|
topic,
|
|
789
836
|
score: scoreTopicSearch(topic, matchReasons),
|
|
790
|
-
matchReasons
|
|
837
|
+
matchReasons,
|
|
838
|
+
source: "topic"
|
|
791
839
|
};
|
|
792
840
|
}
|
|
793
841
|
function mapFactSearchResult(row, normalizedQuery) {
|
|
@@ -802,7 +850,8 @@ function mapFactSearchResult(row, normalizedQuery) {
|
|
|
802
850
|
return {
|
|
803
851
|
fact,
|
|
804
852
|
score: scoreFactSearch(fact, matchReasons),
|
|
805
|
-
matchReasons
|
|
853
|
+
matchReasons,
|
|
854
|
+
source: "fact"
|
|
806
855
|
};
|
|
807
856
|
}
|
|
808
857
|
function mapRecentTopicMessageRow(row) {
|
|
@@ -818,6 +867,9 @@ function mapRecentTopicMessageRow(row) {
|
|
|
818
867
|
message: {
|
|
819
868
|
id: row.id,
|
|
820
869
|
sessionId: row.session_id,
|
|
870
|
+
userId: row.user_id,
|
|
871
|
+
channelType: row.channel_type,
|
|
872
|
+
senderOpenId: row.sender_open_id,
|
|
821
873
|
turnId: row.turn_id,
|
|
822
874
|
parentTurnId: row.parent_turn_id,
|
|
823
875
|
role: row.role,
|
|
@@ -877,6 +929,20 @@ function scoreFactSearch(fact, matchReasons) {
|
|
|
877
929
|
}
|
|
878
930
|
return score;
|
|
879
931
|
}
|
|
932
|
+
function applyUserIdentityMigrations(db) {
|
|
933
|
+
ensureColumn(db, TABLES.messages, "user_id", "TEXT");
|
|
934
|
+
ensureColumn(db, TABLES.messages, "channel_type", "TEXT");
|
|
935
|
+
ensureColumn(db, TABLES.messages, "sender_open_id", "TEXT");
|
|
936
|
+
ensureColumn(db, TABLES.topics, "user_id", "TEXT");
|
|
937
|
+
ensureColumn(db, TABLES.sessionState, "user_id", "TEXT");
|
|
938
|
+
}
|
|
939
|
+
function ensureColumn(db, tableName, columnName, columnSql) {
|
|
940
|
+
const rows = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
941
|
+
if (rows.some((row) => row.name === columnName)) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnSql}`);
|
|
945
|
+
}
|
|
880
946
|
|
|
881
947
|
// ../../packages/summary-refresher/src/index.ts
|
|
882
948
|
var SummaryRefresher = class {
|
|
@@ -1117,10 +1183,12 @@ var STOP_TOKENS = /* @__PURE__ */ new Set([
|
|
|
1117
1183
|
"topics"
|
|
1118
1184
|
]);
|
|
1119
1185
|
|
|
1120
|
-
//
|
|
1186
|
+
// ../../packages/bamdra-memory-context-engine/src/index.ts
|
|
1121
1187
|
var import_node_crypto = require("crypto");
|
|
1122
1188
|
var import_node_os = require("os");
|
|
1123
1189
|
var import_node_path2 = require("node:path");
|
|
1190
|
+
var USER_BIND_GLOBAL_KEY = "__OPENCLAW_BAMDRA_USER_BIND__";
|
|
1191
|
+
var VECTOR_GLOBAL_KEY = "__OPENCLAW_BAMDRA_MEMORY_VECTOR__";
|
|
1124
1192
|
var DEFAULT_DB_PATH = (0, import_node_path2.join)(
|
|
1125
1193
|
(0, import_node_os.homedir)(),
|
|
1126
1194
|
".openclaw",
|
|
@@ -1129,9 +1197,9 @@ var DEFAULT_DB_PATH = (0, import_node_path2.join)(
|
|
|
1129
1197
|
);
|
|
1130
1198
|
function logMemoryEvent(event, details = {}) {
|
|
1131
1199
|
try {
|
|
1132
|
-
console.info("[bamdra-memory]", event, JSON.stringify(details));
|
|
1200
|
+
console.info("[bamdra-openclaw-memory]", event, JSON.stringify(details));
|
|
1133
1201
|
} catch {
|
|
1134
|
-
console.info("[bamdra-memory]", event);
|
|
1202
|
+
console.info("[bamdra-openclaw-memory]", event);
|
|
1135
1203
|
}
|
|
1136
1204
|
}
|
|
1137
1205
|
function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
@@ -1156,7 +1224,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1156
1224
|
}
|
|
1157
1225
|
let hooksRegistered = false;
|
|
1158
1226
|
const plugin = {
|
|
1159
|
-
name: "bamdra-memory
|
|
1227
|
+
name: "bamdra-openclaw-memory",
|
|
1160
1228
|
type: "context-engine",
|
|
1161
1229
|
slot: "memory",
|
|
1162
1230
|
capabilities: ["memory"],
|
|
@@ -1260,6 +1328,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1260
1328
|
throw new Error(`Topic ${topicId} does not belong to session ${sessionId}`);
|
|
1261
1329
|
}
|
|
1262
1330
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1331
|
+
const userId = getResolvedUserId(sessionId);
|
|
1263
1332
|
await cache.setSessionState(sessionId, {
|
|
1264
1333
|
activeTopicId: topicId,
|
|
1265
1334
|
updatedAt: now
|
|
@@ -1273,6 +1342,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1273
1342
|
await store.upsertTopic(updatedTopic);
|
|
1274
1343
|
await store.upsertSessionState({
|
|
1275
1344
|
sessionId,
|
|
1345
|
+
userId,
|
|
1276
1346
|
activeTopicId: topicId,
|
|
1277
1347
|
lastCompactedAt: previousState?.lastCompactedAt ?? null,
|
|
1278
1348
|
lastTurnId: previousState?.lastTurnId ?? null,
|
|
@@ -1287,8 +1357,9 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1287
1357
|
const sessionState = await resolveSessionState(store, cache, args.sessionId);
|
|
1288
1358
|
const resolvedTopicId = args.topicId ?? sessionState?.activeTopicId ?? null;
|
|
1289
1359
|
const resolvedTopic = resolvedTopicId != null ? await store.getTopic(resolvedTopicId) : null;
|
|
1360
|
+
const userId = getResolvedUserId(args.sessionId);
|
|
1290
1361
|
const normalizedScope = normalizeFactScope(args.scope, args.sessionId);
|
|
1291
|
-
const scope = normalizedScope ?? (resolvedTopic != null ? `topic:${resolvedTopic.id}` : "shared");
|
|
1362
|
+
const scope = normalizedScope ?? (resolvedTopic != null ? `topic:${resolvedTopic.id}` : userId != null ? `user:${userId}` : "shared");
|
|
1292
1363
|
const tags = dedupeTextItems([
|
|
1293
1364
|
...resolvedTopic?.labels ?? [],
|
|
1294
1365
|
...args.tags ?? [],
|
|
@@ -1313,6 +1384,15 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1313
1384
|
if (resolvedTopic) {
|
|
1314
1385
|
await refreshTopicSummary(store, summaryRefresher, config, resolvedTopic.id, now);
|
|
1315
1386
|
}
|
|
1387
|
+
pushVectorMemoryRecord({
|
|
1388
|
+
userId,
|
|
1389
|
+
sessionId: args.sessionId,
|
|
1390
|
+
topicId: resolvedTopic?.id ?? null,
|
|
1391
|
+
sourcePath: (0, import_node_path2.join)("user", userId ?? "shared", "facts", `${args.key}.md`),
|
|
1392
|
+
title: args.key,
|
|
1393
|
+
text: args.value,
|
|
1394
|
+
tags
|
|
1395
|
+
});
|
|
1316
1396
|
logMemoryEvent("save-fact", {
|
|
1317
1397
|
sessionId: args.sessionId,
|
|
1318
1398
|
topicId: resolvedTopic?.id ?? null,
|
|
@@ -1346,6 +1426,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1346
1426
|
);
|
|
1347
1427
|
await store.upsertSessionState({
|
|
1348
1428
|
sessionId: args.sessionId,
|
|
1429
|
+
userId: getResolvedUserId(args.sessionId),
|
|
1349
1430
|
activeTopicId: sessionState?.activeTopicId ?? topic.id,
|
|
1350
1431
|
lastCompactedAt: now,
|
|
1351
1432
|
lastTurnId: sessionState?.lastTurnId ?? null,
|
|
@@ -1367,20 +1448,30 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1367
1448
|
const sessionState = await resolveSessionState(store, cache, args.sessionId);
|
|
1368
1449
|
const limit = args.limit ?? 5;
|
|
1369
1450
|
const resolvedTopicId = args.topicId ?? sessionState?.activeTopicId ?? null;
|
|
1451
|
+
const userId = getResolvedUserId(args.sessionId);
|
|
1370
1452
|
const [topics, facts] = await Promise.all([
|
|
1371
1453
|
store.searchTopics(args.sessionId, args.query, limit),
|
|
1372
1454
|
store.searchFacts({
|
|
1373
1455
|
sessionId: args.sessionId,
|
|
1456
|
+
userId,
|
|
1374
1457
|
query: args.query,
|
|
1375
1458
|
topicId: resolvedTopicId,
|
|
1376
1459
|
limit
|
|
1377
1460
|
})
|
|
1378
1461
|
]);
|
|
1462
|
+
const vectors = searchVectorMemory({
|
|
1463
|
+
query: args.query,
|
|
1464
|
+
userId,
|
|
1465
|
+
topicId: resolvedTopicId,
|
|
1466
|
+
limit
|
|
1467
|
+
});
|
|
1379
1468
|
return {
|
|
1380
1469
|
sessionId: args.sessionId,
|
|
1470
|
+
userId,
|
|
1381
1471
|
query: args.query,
|
|
1382
1472
|
topics,
|
|
1383
|
-
facts
|
|
1473
|
+
facts,
|
|
1474
|
+
vectors
|
|
1384
1475
|
};
|
|
1385
1476
|
},
|
|
1386
1477
|
async routeTopic(sessionId, text) {
|
|
@@ -1398,6 +1489,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1398
1489
|
await ensureMigrations();
|
|
1399
1490
|
const sessionState = await resolveSessionState(store, cache, sessionId);
|
|
1400
1491
|
const topic = sessionState?.activeTopicId != null ? await store.getTopic(sessionState.activeTopicId) : null;
|
|
1492
|
+
const userId = getResolvedUserId(sessionId);
|
|
1401
1493
|
const recentMessages = topic != null ? await store.listRecentMessagesForTopic(
|
|
1402
1494
|
topic.id,
|
|
1403
1495
|
config.contextAssembly?.recentTurns ?? 6
|
|
@@ -1405,13 +1497,14 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1405
1497
|
const alwaysFacts = await store.listFactsByScope("global");
|
|
1406
1498
|
const sharedFacts = await store.listFactsByScope("shared");
|
|
1407
1499
|
const sessionFacts = await store.listFactsByScope(`session:${sessionId}`);
|
|
1500
|
+
const userFacts = userId != null ? await store.listFactsByScope(`user:${userId}`) : [];
|
|
1408
1501
|
const scopedTopicFacts = topic != null ? await store.listFactsByScope(`topic:${topic.id}`) : [];
|
|
1409
1502
|
const labelFacts = topic != null ? await store.listFactsByTags(topic.labels, ["always", "topic_bound"]) : [];
|
|
1410
1503
|
return assembler.assemble({
|
|
1411
1504
|
sessionId,
|
|
1412
1505
|
topic,
|
|
1413
1506
|
recentMessages,
|
|
1414
|
-
alwaysFacts: dedupeFacts([...alwaysFacts, ...sharedFacts, ...sessionFacts]),
|
|
1507
|
+
alwaysFacts: dedupeFacts([...alwaysFacts, ...sharedFacts, ...sessionFacts, ...userFacts]),
|
|
1415
1508
|
topicFacts: dedupeFacts([...scopedTopicFacts, ...labelFacts])
|
|
1416
1509
|
});
|
|
1417
1510
|
},
|
|
@@ -1427,9 +1520,10 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1427
1520
|
recentTopics
|
|
1428
1521
|
});
|
|
1429
1522
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1523
|
+
const userId = getResolvedUserId(sessionId);
|
|
1430
1524
|
const topicId = decision.action === "spawn" ? createTopicId(sessionId, text) : decision.topicId;
|
|
1431
1525
|
const messageId = (0, import_node_crypto.randomUUID)();
|
|
1432
|
-
const topicRecord = decision.action === "spawn" ? createSpawnedTopic(sessionId, topicId, text, now, persistedState?.activeTopicId ?? null) : await store.getTopic(topicId);
|
|
1526
|
+
const topicRecord = decision.action === "spawn" ? createSpawnedTopic(sessionId, userId, topicId, text, now, persistedState?.activeTopicId ?? null) : await store.getTopic(topicId);
|
|
1433
1527
|
if (!topicRecord) {
|
|
1434
1528
|
throw new Error(`Unable to resolve topic ${topicId} after routing`);
|
|
1435
1529
|
}
|
|
@@ -1438,6 +1532,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1438
1532
|
} else {
|
|
1439
1533
|
await store.upsertTopic({
|
|
1440
1534
|
...topicRecord,
|
|
1535
|
+
userId,
|
|
1441
1536
|
status: "active",
|
|
1442
1537
|
openLoops: mergeOpenLoops(topicRecord.openLoops, text),
|
|
1443
1538
|
lastActiveAt: now
|
|
@@ -1446,6 +1541,9 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1446
1541
|
const messageRecord = {
|
|
1447
1542
|
id: messageId,
|
|
1448
1543
|
sessionId,
|
|
1544
|
+
userId,
|
|
1545
|
+
channelType: getResolvedChannelType(sessionId),
|
|
1546
|
+
senderOpenId: getResolvedSenderOpenId(sessionId),
|
|
1449
1547
|
turnId: messageId,
|
|
1450
1548
|
parentTurnId: persistedState?.lastTurnId ?? null,
|
|
1451
1549
|
role: "user",
|
|
@@ -1484,12 +1582,22 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1484
1582
|
);
|
|
1485
1583
|
}
|
|
1486
1584
|
await refreshTopicSummary(store, summaryRefresher, config, topicId, now);
|
|
1585
|
+
pushVectorMemoryRecord({
|
|
1586
|
+
userId,
|
|
1587
|
+
sessionId,
|
|
1588
|
+
topicId,
|
|
1589
|
+
sourcePath: (0, import_node_path2.join)("user", userId ?? "shared", "topics", topicId, `${messageId}.md`),
|
|
1590
|
+
title: deriveTopicTitle(text),
|
|
1591
|
+
text,
|
|
1592
|
+
tags: dedupeTextItems(topicRecord.labels)
|
|
1593
|
+
});
|
|
1487
1594
|
await cache.setSessionState(sessionId, {
|
|
1488
1595
|
activeTopicId: topicId,
|
|
1489
1596
|
updatedAt: now
|
|
1490
1597
|
});
|
|
1491
1598
|
await store.upsertSessionState({
|
|
1492
1599
|
sessionId,
|
|
1600
|
+
userId,
|
|
1493
1601
|
activeTopicId: topicId,
|
|
1494
1602
|
lastCompactedAt: persistedState?.lastCompactedAt ?? null,
|
|
1495
1603
|
lastTurnId: messageId,
|
|
@@ -1497,6 +1605,7 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1497
1605
|
});
|
|
1498
1606
|
logMemoryEvent("route-and-track", {
|
|
1499
1607
|
sessionId,
|
|
1608
|
+
userId,
|
|
1500
1609
|
action: decision.action,
|
|
1501
1610
|
reason: decision.reason,
|
|
1502
1611
|
topicId,
|
|
@@ -1519,6 +1628,7 @@ async function resolveSessionState(store, cache, sessionId) {
|
|
|
1519
1628
|
function mapCachedStateToSessionState(sessionId, cached) {
|
|
1520
1629
|
return {
|
|
1521
1630
|
sessionId,
|
|
1631
|
+
userId: getResolvedUserId(sessionId),
|
|
1522
1632
|
activeTopicId: cached.activeTopicId,
|
|
1523
1633
|
lastCompactedAt: null,
|
|
1524
1634
|
lastTurnId: null,
|
|
@@ -1544,12 +1654,50 @@ function normalizeFactScope(scope, sessionId) {
|
|
|
1544
1654
|
if (normalized === "session") {
|
|
1545
1655
|
return `session:${sessionId}`;
|
|
1546
1656
|
}
|
|
1657
|
+
if (normalized === "user") {
|
|
1658
|
+
const userId = getResolvedUserId(sessionId);
|
|
1659
|
+
return userId != null ? `user:${userId}` : null;
|
|
1660
|
+
}
|
|
1547
1661
|
return normalized;
|
|
1548
1662
|
}
|
|
1549
|
-
function
|
|
1663
|
+
function getResolvedIdentity(sessionId) {
|
|
1664
|
+
const api = globalThis[USER_BIND_GLOBAL_KEY];
|
|
1665
|
+
if (!api?.getIdentityForSession) {
|
|
1666
|
+
return null;
|
|
1667
|
+
}
|
|
1668
|
+
return api.getIdentityForSession(sessionId);
|
|
1669
|
+
}
|
|
1670
|
+
function getResolvedUserId(sessionId) {
|
|
1671
|
+
const identity = getResolvedIdentity(sessionId);
|
|
1672
|
+
const userId = identity?.userId;
|
|
1673
|
+
return typeof userId === "string" && userId.trim() ? userId : null;
|
|
1674
|
+
}
|
|
1675
|
+
function getResolvedChannelType(sessionId) {
|
|
1676
|
+
const identity = getResolvedIdentity(sessionId);
|
|
1677
|
+
const channelType = identity?.channelType;
|
|
1678
|
+
return typeof channelType === "string" && channelType.trim() ? channelType : null;
|
|
1679
|
+
}
|
|
1680
|
+
function getResolvedSenderOpenId(sessionId) {
|
|
1681
|
+
const identity = getResolvedIdentity(sessionId);
|
|
1682
|
+
const senderOpenId = identity?.senderOpenId;
|
|
1683
|
+
return typeof senderOpenId === "string" && senderOpenId.trim() ? senderOpenId : null;
|
|
1684
|
+
}
|
|
1685
|
+
function pushVectorMemoryRecord(args) {
|
|
1686
|
+
const api = globalThis[VECTOR_GLOBAL_KEY];
|
|
1687
|
+
api?.upsertMemoryRecord?.(args);
|
|
1688
|
+
}
|
|
1689
|
+
function searchVectorMemory(args) {
|
|
1690
|
+
const api = globalThis[VECTOR_GLOBAL_KEY];
|
|
1691
|
+
if (!api?.search) {
|
|
1692
|
+
return [];
|
|
1693
|
+
}
|
|
1694
|
+
return api.search(args);
|
|
1695
|
+
}
|
|
1696
|
+
function createSpawnedTopic(sessionId, userId, topicId, text, now, parentTopicId) {
|
|
1550
1697
|
return {
|
|
1551
1698
|
id: topicId,
|
|
1552
1699
|
sessionId,
|
|
1700
|
+
userId,
|
|
1553
1701
|
title: deriveTopicTitle(text),
|
|
1554
1702
|
status: "active",
|
|
1555
1703
|
parentTopicId,
|
|
@@ -1772,15 +1920,32 @@ function buildBeforePromptBuildResult(assembled) {
|
|
|
1772
1920
|
}
|
|
1773
1921
|
|
|
1774
1922
|
// src/index.ts
|
|
1923
|
+
var import_node_fs2 = require("node:fs");
|
|
1924
|
+
var import_node_module = require("module");
|
|
1925
|
+
var import_node_os2 = require("os");
|
|
1926
|
+
var import_node_path3 = require("node:path");
|
|
1775
1927
|
var PLUGIN_ID = "bamdra-openclaw-memory";
|
|
1776
1928
|
var ENGINE_GLOBAL_KEY = "__OPENCLAW_BAMDRA_MEMORY_CONTEXT_ENGINE__";
|
|
1777
|
-
var TOOLS_REGISTERED_KEY = /* @__PURE__ */ Symbol.for("bamdra-memory.tools-registered");
|
|
1778
|
-
var ENGINE_REGISTERED_KEY = /* @__PURE__ */ Symbol.for("bamdra-memory.context-engine-registered");
|
|
1929
|
+
var TOOLS_REGISTERED_KEY = /* @__PURE__ */ Symbol.for("bamdra-openclaw-memory.tools-registered");
|
|
1930
|
+
var ENGINE_REGISTERED_KEY = /* @__PURE__ */ Symbol.for("bamdra-openclaw-memory.context-engine-registered");
|
|
1931
|
+
var BOOTSTRAP_STARTED_KEY = /* @__PURE__ */ Symbol.for("bamdra-openclaw-memory.host-bootstrap-started");
|
|
1932
|
+
var SKILL_ID = "bamdra-memory-operator";
|
|
1933
|
+
var REQUIRED_TOOL_NAMES = [
|
|
1934
|
+
"memory_list_topics",
|
|
1935
|
+
"memory_switch_topic",
|
|
1936
|
+
"memory_save_fact",
|
|
1937
|
+
"memory_compact_topic",
|
|
1938
|
+
"memory_search"
|
|
1939
|
+
];
|
|
1940
|
+
var REQUIRED_PLUGIN_IDS = ["bamdra-user-bind"];
|
|
1941
|
+
var OPTIONAL_PLUGIN_IDS = ["bamdra-memory-vector"];
|
|
1942
|
+
var AUTO_PROVISION_PLUGIN_IDS = [...REQUIRED_PLUGIN_IDS, ...OPTIONAL_PLUGIN_IDS];
|
|
1943
|
+
var runtimeRequire = (0, import_node_module.createRequire)(__filename);
|
|
1779
1944
|
function logUnifiedMemoryEvent(event, details = {}) {
|
|
1780
1945
|
try {
|
|
1781
|
-
console.info("[bamdra-memory]", event, JSON.stringify(details));
|
|
1946
|
+
console.info("[bamdra-openclaw-memory]", event, JSON.stringify(details));
|
|
1782
1947
|
} catch {
|
|
1783
|
-
console.info("[bamdra-memory]", event);
|
|
1948
|
+
console.info("[bamdra-openclaw-memory]", event);
|
|
1784
1949
|
}
|
|
1785
1950
|
}
|
|
1786
1951
|
function register(api) {
|
|
@@ -1791,6 +1956,18 @@ async function activate(api) {
|
|
|
1791
1956
|
}
|
|
1792
1957
|
function initializeUnifiedPlugin(api, phase) {
|
|
1793
1958
|
logUnifiedMemoryEvent(`${phase}-plugin`, { id: PLUGIN_ID });
|
|
1959
|
+
if (!api[BOOTSTRAP_STARTED_KEY]) {
|
|
1960
|
+
api[BOOTSTRAP_STARTED_KEY] = true;
|
|
1961
|
+
queueMicrotask(() => {
|
|
1962
|
+
try {
|
|
1963
|
+
bootstrapOpenClawHost();
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
logUnifiedMemoryEvent("host-bootstrap-failed", {
|
|
1966
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1794
1971
|
const engine = brandContextEngine(createContextEngineMemoryV2Plugin(api.pluginConfig ?? api.config, api));
|
|
1795
1972
|
exposeContextEngine(engine);
|
|
1796
1973
|
engine.registerHooks(api);
|
|
@@ -1826,8 +2003,7 @@ function exposeContextEngine(engine) {
|
|
|
1826
2003
|
function registerUnifiedTools(api, engine) {
|
|
1827
2004
|
const definitions = [
|
|
1828
2005
|
createToolDefinitions({
|
|
1829
|
-
|
|
1830
|
-
aliasName: "bamdra_memory_list_topics",
|
|
2006
|
+
name: "memory_list_topics",
|
|
1831
2007
|
description: "List known topics for a session",
|
|
1832
2008
|
parameters: {
|
|
1833
2009
|
type: "object",
|
|
@@ -1842,8 +2018,7 @@ function registerUnifiedTools(api, engine) {
|
|
|
1842
2018
|
}
|
|
1843
2019
|
}),
|
|
1844
2020
|
createToolDefinitions({
|
|
1845
|
-
|
|
1846
|
-
aliasName: "bamdra_memory_switch_topic",
|
|
2021
|
+
name: "memory_switch_topic",
|
|
1847
2022
|
description: "Switch the active topic for a session",
|
|
1848
2023
|
parameters: {
|
|
1849
2024
|
type: "object",
|
|
@@ -1859,8 +2034,7 @@ function registerUnifiedTools(api, engine) {
|
|
|
1859
2034
|
}
|
|
1860
2035
|
}),
|
|
1861
2036
|
createToolDefinitions({
|
|
1862
|
-
|
|
1863
|
-
aliasName: "bamdra_memory_save_fact",
|
|
2037
|
+
name: "memory_save_fact",
|
|
1864
2038
|
description: "Persist a pinned memory fact for the current or selected topic",
|
|
1865
2039
|
parameters: {
|
|
1866
2040
|
type: "object",
|
|
@@ -1886,8 +2060,7 @@ function registerUnifiedTools(api, engine) {
|
|
|
1886
2060
|
}
|
|
1887
2061
|
}),
|
|
1888
2062
|
createToolDefinitions({
|
|
1889
|
-
|
|
1890
|
-
aliasName: "bamdra_memory_compact_topic",
|
|
2063
|
+
name: "memory_compact_topic",
|
|
1891
2064
|
description: "Force refresh the summary for the current or selected topic",
|
|
1892
2065
|
parameters: {
|
|
1893
2066
|
type: "object",
|
|
@@ -1903,8 +2076,7 @@ function registerUnifiedTools(api, engine) {
|
|
|
1903
2076
|
}
|
|
1904
2077
|
}),
|
|
1905
2078
|
createToolDefinitions({
|
|
1906
|
-
|
|
1907
|
-
aliasName: "bamdra_memory_search",
|
|
2079
|
+
name: "memory_search",
|
|
1908
2080
|
description: "Search topics and durable facts for a session",
|
|
1909
2081
|
parameters: {
|
|
1910
2082
|
type: "object",
|
|
@@ -1930,20 +2102,20 @@ function registerUnifiedTools(api, engine) {
|
|
|
1930
2102
|
}
|
|
1931
2103
|
}
|
|
1932
2104
|
function createToolDefinitions(definition) {
|
|
1933
|
-
return [
|
|
1934
|
-
name,
|
|
2105
|
+
return [{
|
|
2106
|
+
name: definition.name,
|
|
1935
2107
|
description: definition.description,
|
|
1936
2108
|
parameters: definition.parameters,
|
|
1937
2109
|
async execute(invocationId, params) {
|
|
1938
2110
|
const result = await definition.execute(params);
|
|
1939
2111
|
logUnifiedMemoryEvent("tool-execute", {
|
|
1940
|
-
name,
|
|
2112
|
+
name: definition.name,
|
|
1941
2113
|
invocationId,
|
|
1942
2114
|
sessionId: params && typeof params === "object" && "sessionId" in params ? params.sessionId : null
|
|
1943
2115
|
});
|
|
1944
2116
|
return asTextResult(result);
|
|
1945
2117
|
}
|
|
1946
|
-
}
|
|
2118
|
+
}];
|
|
1947
2119
|
}
|
|
1948
2120
|
function asTextResult(value) {
|
|
1949
2121
|
return {
|
|
@@ -1955,6 +2127,283 @@ function asTextResult(value) {
|
|
|
1955
2127
|
]
|
|
1956
2128
|
};
|
|
1957
2129
|
}
|
|
2130
|
+
function bootstrapOpenClawHost() {
|
|
2131
|
+
const openclawHome = (0, import_node_path3.resolve)((0, import_node_os2.homedir)(), ".openclaw");
|
|
2132
|
+
const extensionRoot = (0, import_node_path3.join)(openclawHome, "extensions");
|
|
2133
|
+
const memoryRoot = (0, import_node_path3.join)(openclawHome, "memory");
|
|
2134
|
+
const pluginRuntimeRoot = (0, import_node_path3.resolve)((0, import_node_path3.dirname)(__dirname));
|
|
2135
|
+
const configPath = (0, import_node_path3.join)(openclawHome, "openclaw.json");
|
|
2136
|
+
const globalSkillsDir = (0, import_node_path3.join)(openclawHome, "skills");
|
|
2137
|
+
const bundledSkillDir = (0, import_node_path3.join)((0, import_node_path3.resolve)((0, import_node_path3.dirname)(__dirname)), "skills", SKILL_ID);
|
|
2138
|
+
const targetSkillDir = (0, import_node_path3.join)(globalSkillsDir, SKILL_ID);
|
|
2139
|
+
const forceBootstrap = process.env.OPENCLAW_BAMDRA_MEMORY_FORCE_BOOTSTRAP === "1";
|
|
2140
|
+
if (!forceBootstrap && !pluginRuntimeRoot.startsWith(extensionRoot)) {
|
|
2141
|
+
logUnifiedMemoryEvent("host-bootstrap-skipped", {
|
|
2142
|
+
reason: "non-installed-runtime",
|
|
2143
|
+
pluginRuntimeRoot
|
|
2144
|
+
});
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
if (!(0, import_node_fs2.existsSync)(configPath)) {
|
|
2148
|
+
logUnifiedMemoryEvent("host-bootstrap-skipped", { reason: "missing-openclaw-config" });
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
(0, import_node_fs2.mkdirSync)(openclawHome, { recursive: true });
|
|
2152
|
+
(0, import_node_fs2.mkdirSync)(extensionRoot, { recursive: true });
|
|
2153
|
+
(0, import_node_fs2.mkdirSync)(memoryRoot, { recursive: true });
|
|
2154
|
+
materializeBundledDependencyPlugins(pluginRuntimeRoot, extensionRoot);
|
|
2155
|
+
materializeBundledSkill(bundledSkillDir, targetSkillDir);
|
|
2156
|
+
const original = (0, import_node_fs2.readFileSync)(configPath, "utf8");
|
|
2157
|
+
const config = JSON.parse(original);
|
|
2158
|
+
const changed = ensureHostConfig(config, targetSkillDir);
|
|
2159
|
+
if (!changed) {
|
|
2160
|
+
logUnifiedMemoryEvent("host-bootstrap-noop", { configPath });
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
(0, import_node_fs2.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}
|
|
2164
|
+
`, "utf8");
|
|
2165
|
+
logUnifiedMemoryEvent("host-bootstrap-updated", {
|
|
2166
|
+
configPath,
|
|
2167
|
+
targetSkillDir,
|
|
2168
|
+
pluginId: PLUGIN_ID
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
function materializeBundledDependencyPlugins(pluginRuntimeRoot, extensionRoot) {
|
|
2172
|
+
for (const pluginId of AUTO_PROVISION_PLUGIN_IDS) {
|
|
2173
|
+
const targetDir = (0, import_node_path3.join)(extensionRoot, pluginId);
|
|
2174
|
+
if ((0, import_node_fs2.existsSync)(targetDir)) {
|
|
2175
|
+
continue;
|
|
2176
|
+
}
|
|
2177
|
+
const sourceDir = resolveBundledDependencySource(pluginRuntimeRoot, pluginId);
|
|
2178
|
+
if (!sourceDir) {
|
|
2179
|
+
logUnifiedMemoryEvent("dependency-plugin-copy-skipped", {
|
|
2180
|
+
pluginId,
|
|
2181
|
+
reason: "source-not-found"
|
|
2182
|
+
});
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(targetDir), { recursive: true });
|
|
2186
|
+
(0, import_node_fs2.cpSync)(sourceDir, targetDir, { recursive: true });
|
|
2187
|
+
logUnifiedMemoryEvent("dependency-plugin-copied", {
|
|
2188
|
+
pluginId,
|
|
2189
|
+
sourceDir,
|
|
2190
|
+
targetDir
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function resolveBundledDependencySource(pluginRuntimeRoot, pluginId) {
|
|
2195
|
+
const packageNameByPluginId = {
|
|
2196
|
+
"bamdra-user-bind": "@bamdra/bamdra-user-bind",
|
|
2197
|
+
"bamdra-memory-vector": "@bamdra/bamdra-memory-vector"
|
|
2198
|
+
};
|
|
2199
|
+
const candidateRoots = [];
|
|
2200
|
+
const packageName = packageNameByPluginId[pluginId];
|
|
2201
|
+
if (packageName) {
|
|
2202
|
+
try {
|
|
2203
|
+
const packageJsonPath = runtimeRequire.resolve(`${packageName}/package.json`);
|
|
2204
|
+
candidateRoots.push((0, import_node_path3.dirname)(packageJsonPath));
|
|
2205
|
+
} catch {
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
candidateRoots.push((0, import_node_path3.join)(pluginRuntimeRoot, "bundled-plugins", pluginId));
|
|
2209
|
+
candidateRoots.push((0, import_node_path3.resolve)(pluginRuntimeRoot, "..", "..", "..", pluginId));
|
|
2210
|
+
for (const root of candidateRoots) {
|
|
2211
|
+
const distDir = (0, import_node_fs2.existsSync)((0, import_node_path3.join)(root, "dist")) ? (0, import_node_path3.join)(root, "dist") : root;
|
|
2212
|
+
if ((0, import_node_fs2.existsSync)((0, import_node_path3.join)(distDir, "index.js")) && (0, import_node_fs2.existsSync)((0, import_node_path3.join)(distDir, "openclaw.plugin.json")) && (0, import_node_fs2.existsSync)((0, import_node_path3.join)(distDir, "package.json"))) {
|
|
2213
|
+
return distDir;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
return null;
|
|
2217
|
+
}
|
|
2218
|
+
function materializeBundledSkill(sourceDir, targetDir) {
|
|
2219
|
+
if (!(0, import_node_fs2.existsSync)(sourceDir)) {
|
|
2220
|
+
logUnifiedMemoryEvent("skill-copy-skipped", {
|
|
2221
|
+
reason: "missing-bundled-skill",
|
|
2222
|
+
sourceDir
|
|
2223
|
+
});
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
if ((0, import_node_fs2.existsSync)(targetDir)) {
|
|
2227
|
+
logUnifiedMemoryEvent("skill-copy-skipped", {
|
|
2228
|
+
reason: "target-exists",
|
|
2229
|
+
targetDir
|
|
2230
|
+
});
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(targetDir), { recursive: true });
|
|
2234
|
+
(0, import_node_fs2.cpSync)(sourceDir, targetDir, { recursive: true });
|
|
2235
|
+
logUnifiedMemoryEvent("skill-copied", { sourceDir, targetDir });
|
|
2236
|
+
}
|
|
2237
|
+
function ensureHostConfig(config, targetSkillDir) {
|
|
2238
|
+
let changed = false;
|
|
2239
|
+
const plugins = ensureObject(config, "plugins");
|
|
2240
|
+
const tools = ensureObject(config, "tools");
|
|
2241
|
+
const skills = ensureObject(config, "skills");
|
|
2242
|
+
const skillsLoad = ensureObject(skills, "load");
|
|
2243
|
+
const agents = ensureObject(config, "agents");
|
|
2244
|
+
const entries = ensureObject(plugins, "entries");
|
|
2245
|
+
const installs = ensureObject(plugins, "installs");
|
|
2246
|
+
const load = ensureObject(plugins, "load");
|
|
2247
|
+
const slots = ensureObject(plugins, "slots");
|
|
2248
|
+
const pluginEntry = ensureObject(entries, PLUGIN_ID);
|
|
2249
|
+
const userBindEntry = ensureObject(entries, REQUIRED_PLUGIN_IDS[0]);
|
|
2250
|
+
const vectorEntry = ensureObject(entries, OPTIONAL_PLUGIN_IDS[0]);
|
|
2251
|
+
const pluginConfig = ensureObject(pluginEntry, "config");
|
|
2252
|
+
const pluginStore = ensureObject(pluginConfig, "store");
|
|
2253
|
+
const pluginCache = ensureObject(pluginConfig, "cache");
|
|
2254
|
+
changed = ensureArrayIncludes(plugins, "allow", PLUGIN_ID) || changed;
|
|
2255
|
+
for (const dependencyId of REQUIRED_PLUGIN_IDS) {
|
|
2256
|
+
changed = ensureArrayIncludes(plugins, "allow", dependencyId) || changed;
|
|
2257
|
+
const dependencyEntry = dependencyId === REQUIRED_PLUGIN_IDS[0] ? userBindEntry : ensureObject(entries, dependencyId);
|
|
2258
|
+
if (dependencyEntry.enabled !== true) {
|
|
2259
|
+
dependencyEntry.enabled = true;
|
|
2260
|
+
changed = true;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
for (const optionalId of OPTIONAL_PLUGIN_IDS) {
|
|
2264
|
+
changed = ensureArrayIncludes(plugins, "allow", optionalId) || changed;
|
|
2265
|
+
}
|
|
2266
|
+
changed = ensureArrayIncludes(plugins, "deny", "memory-core") || changed;
|
|
2267
|
+
changed = ensureArrayIncludes(load, "paths", (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".openclaw", "extensions")) || changed;
|
|
2268
|
+
changed = ensureArrayIncludes(skillsLoad, "extraDirs", (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".openclaw", "skills")) || changed;
|
|
2269
|
+
if (slots.memory !== PLUGIN_ID) {
|
|
2270
|
+
slots.memory = PLUGIN_ID;
|
|
2271
|
+
changed = true;
|
|
2272
|
+
}
|
|
2273
|
+
if (slots.contextEngine !== PLUGIN_ID) {
|
|
2274
|
+
slots.contextEngine = PLUGIN_ID;
|
|
2275
|
+
changed = true;
|
|
2276
|
+
}
|
|
2277
|
+
if (pluginEntry.enabled !== true) {
|
|
2278
|
+
pluginEntry.enabled = true;
|
|
2279
|
+
changed = true;
|
|
2280
|
+
}
|
|
2281
|
+
if (pluginConfig.enabled !== true) {
|
|
2282
|
+
pluginConfig.enabled = true;
|
|
2283
|
+
changed = true;
|
|
2284
|
+
}
|
|
2285
|
+
if (pluginStore.provider !== "sqlite") {
|
|
2286
|
+
pluginStore.provider = "sqlite";
|
|
2287
|
+
changed = true;
|
|
2288
|
+
}
|
|
2289
|
+
if (typeof pluginStore.path !== "string" || pluginStore.path.length === 0) {
|
|
2290
|
+
pluginStore.path = "~/.openclaw/memory/main.sqlite";
|
|
2291
|
+
changed = true;
|
|
2292
|
+
}
|
|
2293
|
+
if (pluginCache.provider !== "memory") {
|
|
2294
|
+
pluginCache.provider = "memory";
|
|
2295
|
+
changed = true;
|
|
2296
|
+
}
|
|
2297
|
+
if (typeof pluginCache.maxSessions !== "number") {
|
|
2298
|
+
pluginCache.maxSessions = 128;
|
|
2299
|
+
changed = true;
|
|
2300
|
+
}
|
|
2301
|
+
if (typeof userBindEntry.config !== "object" || userBindEntry.config == null) {
|
|
2302
|
+
userBindEntry.config = {
|
|
2303
|
+
enabled: true,
|
|
2304
|
+
adminAgents: []
|
|
2305
|
+
};
|
|
2306
|
+
changed = true;
|
|
2307
|
+
}
|
|
2308
|
+
if (vectorEntry.enabled !== false) {
|
|
2309
|
+
vectorEntry.enabled = false;
|
|
2310
|
+
changed = true;
|
|
2311
|
+
}
|
|
2312
|
+
if (typeof vectorEntry.config !== "object" || vectorEntry.config == null) {
|
|
2313
|
+
vectorEntry.config = {
|
|
2314
|
+
enabled: false,
|
|
2315
|
+
markdownRoot: "~/.openclaw/memory/vector/markdown",
|
|
2316
|
+
privateMarkdownRoot: "~/.openclaw/memory/vector/markdown/private",
|
|
2317
|
+
sharedMarkdownRoot: "~/.openclaw/memory/vector/markdown/shared",
|
|
2318
|
+
indexPath: "~/.openclaw/memory/vector/index.json",
|
|
2319
|
+
topK: 5
|
|
2320
|
+
};
|
|
2321
|
+
changed = true;
|
|
2322
|
+
} else {
|
|
2323
|
+
const vectorConfig = ensureObject(vectorEntry, "config");
|
|
2324
|
+
if (vectorConfig.enabled !== false) {
|
|
2325
|
+
vectorConfig.enabled = false;
|
|
2326
|
+
changed = true;
|
|
2327
|
+
}
|
|
2328
|
+
if (typeof vectorConfig.markdownRoot !== "string" || vectorConfig.markdownRoot.length === 0) {
|
|
2329
|
+
vectorConfig.markdownRoot = "~/.openclaw/memory/vector/markdown";
|
|
2330
|
+
changed = true;
|
|
2331
|
+
}
|
|
2332
|
+
if (typeof vectorConfig.privateMarkdownRoot !== "string" || vectorConfig.privateMarkdownRoot.length === 0) {
|
|
2333
|
+
vectorConfig.privateMarkdownRoot = "~/.openclaw/memory/vector/markdown/private";
|
|
2334
|
+
changed = true;
|
|
2335
|
+
}
|
|
2336
|
+
if (typeof vectorConfig.sharedMarkdownRoot !== "string" || vectorConfig.sharedMarkdownRoot.length === 0) {
|
|
2337
|
+
vectorConfig.sharedMarkdownRoot = "~/.openclaw/memory/vector/markdown/shared";
|
|
2338
|
+
changed = true;
|
|
2339
|
+
}
|
|
2340
|
+
if (typeof vectorConfig.indexPath !== "string" || vectorConfig.indexPath.length === 0) {
|
|
2341
|
+
vectorConfig.indexPath = "~/.openclaw/memory/vector/index.json";
|
|
2342
|
+
changed = true;
|
|
2343
|
+
}
|
|
2344
|
+
if (typeof vectorConfig.topK !== "number") {
|
|
2345
|
+
vectorConfig.topK = 5;
|
|
2346
|
+
changed = true;
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
if (PLUGIN_ID in installs) {
|
|
2350
|
+
}
|
|
2351
|
+
changed = ensureToolAllowlist(tools) || changed;
|
|
2352
|
+
changed = ensureAgentSkills(agents, SKILL_ID) || changed;
|
|
2353
|
+
if (!(0, import_node_fs2.existsSync)(targetSkillDir)) {
|
|
2354
|
+
logUnifiedMemoryEvent("host-bootstrap-skill-pending", { targetSkillDir });
|
|
2355
|
+
}
|
|
2356
|
+
return changed;
|
|
2357
|
+
}
|
|
2358
|
+
function ensureToolAllowlist(tools) {
|
|
2359
|
+
let changed = false;
|
|
2360
|
+
for (const toolName of REQUIRED_TOOL_NAMES) {
|
|
2361
|
+
changed = ensureArrayIncludes(tools, "allow", toolName) || changed;
|
|
2362
|
+
}
|
|
2363
|
+
return changed;
|
|
2364
|
+
}
|
|
2365
|
+
function ensureAgentSkills(agents, skillId) {
|
|
2366
|
+
const list = Array.isArray(agents.list) ? agents.list : [];
|
|
2367
|
+
let changed = false;
|
|
2368
|
+
for (const item of list) {
|
|
2369
|
+
if (!item || typeof item !== "object") {
|
|
2370
|
+
continue;
|
|
2371
|
+
}
|
|
2372
|
+
const agent = item;
|
|
2373
|
+
const currentSkills = Array.isArray(agent.skills) ? [...agent.skills] : [];
|
|
2374
|
+
if (!Array.isArray(agent.skills)) {
|
|
2375
|
+
agent.skills = currentSkills;
|
|
2376
|
+
changed = true;
|
|
2377
|
+
}
|
|
2378
|
+
if (!currentSkills.includes(skillId)) {
|
|
2379
|
+
currentSkills.push(skillId);
|
|
2380
|
+
agent.skills = currentSkills;
|
|
2381
|
+
changed = true;
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
return changed;
|
|
2385
|
+
}
|
|
2386
|
+
function ensureObject(parent, key) {
|
|
2387
|
+
const current = parent[key];
|
|
2388
|
+
if (current && typeof current === "object" && !Array.isArray(current)) {
|
|
2389
|
+
return current;
|
|
2390
|
+
}
|
|
2391
|
+
const next = {};
|
|
2392
|
+
parent[key] = next;
|
|
2393
|
+
return next;
|
|
2394
|
+
}
|
|
2395
|
+
function ensureArrayIncludes(parent, key, value) {
|
|
2396
|
+
const current = Array.isArray(parent[key]) ? [...parent[key]] : [];
|
|
2397
|
+
if (current.includes(value)) {
|
|
2398
|
+
if (!Array.isArray(parent[key])) {
|
|
2399
|
+
parent[key] = current;
|
|
2400
|
+
}
|
|
2401
|
+
return false;
|
|
2402
|
+
}
|
|
2403
|
+
current.push(value);
|
|
2404
|
+
parent[key] = current;
|
|
2405
|
+
return true;
|
|
2406
|
+
}
|
|
1958
2407
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1959
2408
|
module.exports = {activate,
|
|
1960
2409
|
register};
|