memorylake-openclaw 0.0.6 → 0.0.7
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/index.ts +113 -42
- package/package.json +1 -1
- package/skills/agent-memorylake-config/SKILL.md +43 -0
package/index.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* - CLI: openclaw memorylake search, openclaw memorylake stats
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
13
15
|
import got from "got";
|
|
14
16
|
import { Type } from "@sinclair/typebox";
|
|
15
17
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
@@ -530,6 +532,52 @@ const memoryPlugin = {
|
|
|
530
532
|
const cfg = memoryLakeConfigSchema.parse(api.pluginConfig);
|
|
531
533
|
const provider: MemoryLakeProvider = new PlatformProvider(cfg.host, cfg.apiKey, cfg.projectId);
|
|
532
534
|
|
|
535
|
+
// Provider cache: avoids re-creating providers for the same host+apiKey+projectId
|
|
536
|
+
const providerCache = new Map<string, MemoryLakeProvider>();
|
|
537
|
+
const globalProviderKey = `${cfg.host}|${cfg.apiKey}|${cfg.projectId}`;
|
|
538
|
+
providerCache.set(globalProviderKey, provider);
|
|
539
|
+
|
|
540
|
+
function getProvider(effectiveCfg: MemoryLakeConfig): MemoryLakeProvider {
|
|
541
|
+
const key = `${effectiveCfg.host}|${effectiveCfg.apiKey}|${effectiveCfg.projectId}`;
|
|
542
|
+
let p = providerCache.get(key);
|
|
543
|
+
if (!p) {
|
|
544
|
+
p = new PlatformProvider(effectiveCfg.host, effectiveCfg.apiKey, effectiveCfg.projectId);
|
|
545
|
+
providerCache.set(key, p);
|
|
546
|
+
}
|
|
547
|
+
return p;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function resolveConfig(ctx: any): MemoryLakeConfig {
|
|
551
|
+
const workspaceDir = ctx?.workspaceDir;
|
|
552
|
+
if (!workspaceDir) return cfg;
|
|
553
|
+
|
|
554
|
+
const localPath = path.join(workspaceDir, ".memorylake", "config.json");
|
|
555
|
+
if (!fs.existsSync(localPath)) return cfg;
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
const raw = JSON.parse(fs.readFileSync(localPath, "utf-8"));
|
|
559
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
560
|
+
api.logger.warn(
|
|
561
|
+
`memorylake-openclaw: workspace config exists but is not a JSON object; falling back to global config (path: ${localPath})`,
|
|
562
|
+
);
|
|
563
|
+
return cfg;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const allowed = new Set(ALLOWED_KEYS);
|
|
567
|
+
const overrides: Record<string, unknown> = {};
|
|
568
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
569
|
+
if (allowed.has(key)) overrides[key] = value;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return { ...cfg, ...overrides } as MemoryLakeConfig;
|
|
573
|
+
} catch {
|
|
574
|
+
api.logger.warn(
|
|
575
|
+
`memorylake-openclaw: failed to parse workspace config JSON; falling back to global config (path: ${localPath})`,
|
|
576
|
+
);
|
|
577
|
+
return cfg;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
533
581
|
// Track current session ID for tool-level session scoping
|
|
534
582
|
let currentSessionId: string | undefined;
|
|
535
583
|
|
|
@@ -538,9 +586,9 @@ const memoryPlugin = {
|
|
|
538
586
|
);
|
|
539
587
|
|
|
540
588
|
// Helper: build add options
|
|
541
|
-
function buildAddOptions(userIdOverride?: string, sessionId?: string): AddOptions {
|
|
589
|
+
function buildAddOptions(effectiveCfg: MemoryLakeConfig, userIdOverride?: string, sessionId?: string): AddOptions {
|
|
542
590
|
const opts: AddOptions = {
|
|
543
|
-
user_id: userIdOverride ||
|
|
591
|
+
user_id: userIdOverride || effectiveCfg.userId,
|
|
544
592
|
infer: true,
|
|
545
593
|
metadata: { source: "OPENCLAW" },
|
|
546
594
|
};
|
|
@@ -550,14 +598,15 @@ const memoryPlugin = {
|
|
|
550
598
|
|
|
551
599
|
// Helper: build search options
|
|
552
600
|
function buildSearchOptions(
|
|
601
|
+
effectiveCfg: MemoryLakeConfig,
|
|
553
602
|
userIdOverride?: string,
|
|
554
603
|
limit?: number,
|
|
555
604
|
): SearchOptions {
|
|
556
605
|
return {
|
|
557
|
-
user_id: userIdOverride ||
|
|
558
|
-
top_k: limit ??
|
|
559
|
-
threshold:
|
|
560
|
-
rerank:
|
|
606
|
+
user_id: userIdOverride || effectiveCfg.userId,
|
|
607
|
+
top_k: limit ?? effectiveCfg.topK,
|
|
608
|
+
threshold: effectiveCfg.searchThreshold,
|
|
609
|
+
rerank: effectiveCfg.rerank,
|
|
561
610
|
};
|
|
562
611
|
}
|
|
563
612
|
|
|
@@ -566,7 +615,7 @@ const memoryPlugin = {
|
|
|
566
615
|
// ========================================================================
|
|
567
616
|
|
|
568
617
|
api.registerTool(
|
|
569
|
-
{
|
|
618
|
+
(ctx) => ({
|
|
570
619
|
name: "memory_search",
|
|
571
620
|
label: "Memory Search",
|
|
572
621
|
description:
|
|
@@ -596,6 +645,8 @@ const memoryPlugin = {
|
|
|
596
645
|
),
|
|
597
646
|
}),
|
|
598
647
|
async execute(_toolCallId, params) {
|
|
648
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
649
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
599
650
|
const { query, limit, userId, scope = "all" } = params as {
|
|
600
651
|
query: string;
|
|
601
652
|
limit?: number;
|
|
@@ -604,9 +655,9 @@ const memoryPlugin = {
|
|
|
604
655
|
};
|
|
605
656
|
|
|
606
657
|
try {
|
|
607
|
-
const results = await
|
|
658
|
+
const results = await effectiveProvider.search(
|
|
608
659
|
query,
|
|
609
|
-
buildSearchOptions(userId, limit),
|
|
660
|
+
buildSearchOptions(effectiveCfg, userId, limit),
|
|
610
661
|
);
|
|
611
662
|
|
|
612
663
|
if (!results || results.length === 0) {
|
|
@@ -652,12 +703,12 @@ const memoryPlugin = {
|
|
|
652
703
|
};
|
|
653
704
|
}
|
|
654
705
|
},
|
|
655
|
-
},
|
|
706
|
+
}),
|
|
656
707
|
{ name: "memory_search" },
|
|
657
708
|
);
|
|
658
709
|
|
|
659
710
|
api.registerTool(
|
|
660
|
-
{
|
|
711
|
+
(ctx) => ({
|
|
661
712
|
name: "memory_store",
|
|
662
713
|
label: "Memory Store",
|
|
663
714
|
description:
|
|
@@ -676,6 +727,8 @@ const memoryPlugin = {
|
|
|
676
727
|
),
|
|
677
728
|
}),
|
|
678
729
|
async execute(_toolCallId, params) {
|
|
730
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
731
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
679
732
|
const { text, userId } = params as {
|
|
680
733
|
text: string;
|
|
681
734
|
userId?: string;
|
|
@@ -683,9 +736,9 @@ const memoryPlugin = {
|
|
|
683
736
|
};
|
|
684
737
|
|
|
685
738
|
try {
|
|
686
|
-
const result = await
|
|
739
|
+
const result = await effectiveProvider.add(
|
|
687
740
|
[{ role: "user", content: text }],
|
|
688
|
-
buildAddOptions(userId, currentSessionId),
|
|
741
|
+
buildAddOptions(effectiveCfg, userId, currentSessionId),
|
|
689
742
|
);
|
|
690
743
|
|
|
691
744
|
const count = result.results?.length ?? 0;
|
|
@@ -716,12 +769,12 @@ const memoryPlugin = {
|
|
|
716
769
|
};
|
|
717
770
|
}
|
|
718
771
|
},
|
|
719
|
-
},
|
|
772
|
+
}),
|
|
720
773
|
{ name: "memory_store" },
|
|
721
774
|
);
|
|
722
775
|
|
|
723
776
|
api.registerTool(
|
|
724
|
-
{
|
|
777
|
+
(ctx) => ({
|
|
725
778
|
name: "memory_get",
|
|
726
779
|
label: "Memory Get",
|
|
727
780
|
description: "Retrieve a specific memory by its ID from MemoryLake.",
|
|
@@ -729,10 +782,12 @@ const memoryPlugin = {
|
|
|
729
782
|
memoryId: Type.String({ description: "The memory ID to retrieve" }),
|
|
730
783
|
}),
|
|
731
784
|
async execute(_toolCallId, params) {
|
|
785
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
786
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
732
787
|
const { memoryId } = params as { memoryId: string };
|
|
733
788
|
|
|
734
789
|
try {
|
|
735
|
-
const memory = await
|
|
790
|
+
const memory = await effectiveProvider.get(memoryId);
|
|
736
791
|
|
|
737
792
|
return {
|
|
738
793
|
content: [
|
|
@@ -755,12 +810,12 @@ const memoryPlugin = {
|
|
|
755
810
|
};
|
|
756
811
|
}
|
|
757
812
|
},
|
|
758
|
-
},
|
|
813
|
+
}),
|
|
759
814
|
{ name: "memory_get" },
|
|
760
815
|
);
|
|
761
816
|
|
|
762
817
|
api.registerTool(
|
|
763
|
-
{
|
|
818
|
+
(ctx) => ({
|
|
764
819
|
name: "memory_list",
|
|
765
820
|
label: "Memory List",
|
|
766
821
|
description:
|
|
@@ -784,11 +839,13 @@ const memoryPlugin = {
|
|
|
784
839
|
),
|
|
785
840
|
}),
|
|
786
841
|
async execute(_toolCallId, params) {
|
|
842
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
843
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
787
844
|
const { userId, scope = "all" } = params as { userId?: string; scope?: "session" | "long-term" | "all" };
|
|
788
845
|
|
|
789
846
|
try {
|
|
790
|
-
const uid = userId ||
|
|
791
|
-
const memories = await
|
|
847
|
+
const uid = userId || effectiveCfg.userId;
|
|
848
|
+
const memories = await effectiveProvider.getAll({ user_id: uid });
|
|
792
849
|
|
|
793
850
|
if (!memories || memories.length === 0) {
|
|
794
851
|
return {
|
|
@@ -833,12 +890,12 @@ const memoryPlugin = {
|
|
|
833
890
|
};
|
|
834
891
|
}
|
|
835
892
|
},
|
|
836
|
-
},
|
|
893
|
+
}),
|
|
837
894
|
{ name: "memory_list" },
|
|
838
895
|
);
|
|
839
896
|
|
|
840
897
|
api.registerTool(
|
|
841
|
-
{
|
|
898
|
+
(ctx) => ({
|
|
842
899
|
name: "memory_forget",
|
|
843
900
|
label: "Memory Forget",
|
|
844
901
|
description:
|
|
@@ -847,10 +904,12 @@ const memoryPlugin = {
|
|
|
847
904
|
memoryId: Type.String({ description: "Memory ID to delete" }),
|
|
848
905
|
}),
|
|
849
906
|
async execute(_toolCallId, params) {
|
|
907
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
908
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
850
909
|
const { memoryId } = params as { memoryId: string };
|
|
851
910
|
|
|
852
911
|
try {
|
|
853
|
-
await
|
|
912
|
+
await effectiveProvider.delete(memoryId);
|
|
854
913
|
return {
|
|
855
914
|
content: [
|
|
856
915
|
{ type: "text", text: `Memory ${memoryId} forgotten.` },
|
|
@@ -869,12 +928,12 @@ const memoryPlugin = {
|
|
|
869
928
|
};
|
|
870
929
|
}
|
|
871
930
|
},
|
|
872
|
-
},
|
|
931
|
+
}),
|
|
873
932
|
{ name: "memory_forget" },
|
|
874
933
|
);
|
|
875
934
|
|
|
876
935
|
api.registerTool(
|
|
877
|
-
{
|
|
936
|
+
(ctx) => ({
|
|
878
937
|
name: "document_search",
|
|
879
938
|
label: "Document Search",
|
|
880
939
|
description:
|
|
@@ -889,12 +948,14 @@ const memoryPlugin = {
|
|
|
889
948
|
),
|
|
890
949
|
}),
|
|
891
950
|
async execute(_toolCallId, params) {
|
|
951
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
952
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
892
953
|
const { query, topN } = params as { query: string; topN?: number };
|
|
893
954
|
|
|
894
955
|
try {
|
|
895
|
-
const response = await
|
|
956
|
+
const response = await effectiveProvider.searchDocuments(
|
|
896
957
|
query,
|
|
897
|
-
topN ??
|
|
958
|
+
topN ?? effectiveCfg.topK,
|
|
898
959
|
);
|
|
899
960
|
|
|
900
961
|
if (!response.results || response.results.length === 0) {
|
|
@@ -929,12 +990,12 @@ const memoryPlugin = {
|
|
|
929
990
|
};
|
|
930
991
|
}
|
|
931
992
|
},
|
|
932
|
-
},
|
|
993
|
+
}),
|
|
933
994
|
{ name: "document_search" },
|
|
934
995
|
);
|
|
935
996
|
|
|
936
997
|
api.registerTool(
|
|
937
|
-
{
|
|
998
|
+
(ctx) => ({
|
|
938
999
|
name: "advanced_web_search",
|
|
939
1000
|
label: "Advanced Web Search",
|
|
940
1001
|
description:
|
|
@@ -986,6 +1047,8 @@ const memoryPlugin = {
|
|
|
986
1047
|
),
|
|
987
1048
|
}),
|
|
988
1049
|
async execute(_toolCallId, params) {
|
|
1050
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
1051
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
989
1052
|
const {
|
|
990
1053
|
query,
|
|
991
1054
|
domain: rawDomain,
|
|
@@ -1005,18 +1068,18 @@ const memoryPlugin = {
|
|
|
1005
1068
|
: normalizeWebSearchDomain(rawDomain);
|
|
1006
1069
|
|
|
1007
1070
|
try {
|
|
1008
|
-
const response = await
|
|
1071
|
+
const response = await effectiveProvider.searchWeb(query, {
|
|
1009
1072
|
domain,
|
|
1010
|
-
max_results: maxResults ??
|
|
1073
|
+
max_results: maxResults ?? effectiveCfg.topK,
|
|
1011
1074
|
start_date: startDate,
|
|
1012
1075
|
end_date: endDate,
|
|
1013
|
-
include_domains:
|
|
1014
|
-
exclude_domains:
|
|
1076
|
+
include_domains: effectiveCfg.webSearchIncludeDomains,
|
|
1077
|
+
exclude_domains: effectiveCfg.webSearchExcludeDomains,
|
|
1015
1078
|
user_location:
|
|
1016
|
-
|
|
1079
|
+
effectiveCfg.webSearchCountry || effectiveCfg.webSearchTimezone
|
|
1017
1080
|
? {
|
|
1018
|
-
country:
|
|
1019
|
-
timezone:
|
|
1081
|
+
country: effectiveCfg.webSearchCountry,
|
|
1082
|
+
timezone: effectiveCfg.webSearchTimezone,
|
|
1020
1083
|
}
|
|
1021
1084
|
: undefined,
|
|
1022
1085
|
});
|
|
@@ -1057,7 +1120,7 @@ const memoryPlugin = {
|
|
|
1057
1120
|
};
|
|
1058
1121
|
}
|
|
1059
1122
|
},
|
|
1060
|
-
},
|
|
1123
|
+
}),
|
|
1061
1124
|
{ optional: true },
|
|
1062
1125
|
);
|
|
1063
1126
|
|
|
@@ -1081,7 +1144,7 @@ const memoryPlugin = {
|
|
|
1081
1144
|
const limit = parseInt(opts.limit, 10);
|
|
1082
1145
|
const results = await provider.search(
|
|
1083
1146
|
query,
|
|
1084
|
-
buildSearchOptions(undefined, limit),
|
|
1147
|
+
buildSearchOptions(cfg, undefined, limit),
|
|
1085
1148
|
);
|
|
1086
1149
|
|
|
1087
1150
|
if (!results.length) {
|
|
@@ -1133,13 +1196,17 @@ const memoryPlugin = {
|
|
|
1133
1196
|
api.on("before_agent_start", async (event, ctx) => {
|
|
1134
1197
|
if (!event.prompt || event.prompt.length < 5) return;
|
|
1135
1198
|
|
|
1199
|
+
// Resolve per-workspace config override
|
|
1200
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
1201
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
1202
|
+
|
|
1136
1203
|
// Track session ID
|
|
1137
1204
|
const sessionId = (ctx as any)?.sessionKey ?? undefined;
|
|
1138
1205
|
if (sessionId) currentSessionId = sessionId;
|
|
1139
1206
|
|
|
1140
1207
|
const [memoryResult, docResult] = await Promise.allSettled([
|
|
1141
|
-
|
|
1142
|
-
|
|
1208
|
+
effectiveProvider.search(event.prompt, buildSearchOptions(effectiveCfg)),
|
|
1209
|
+
effectiveProvider.searchDocuments(event.prompt, effectiveCfg.topK),
|
|
1143
1210
|
]);
|
|
1144
1211
|
|
|
1145
1212
|
const contextParts: string[] = [];
|
|
@@ -1183,6 +1250,10 @@ const memoryPlugin = {
|
|
|
1183
1250
|
return;
|
|
1184
1251
|
}
|
|
1185
1252
|
|
|
1253
|
+
// Resolve per-workspace config override
|
|
1254
|
+
const effectiveCfg = resolveConfig(ctx);
|
|
1255
|
+
const effectiveProvider = getProvider(effectiveCfg);
|
|
1256
|
+
|
|
1186
1257
|
// Track session ID
|
|
1187
1258
|
const sessionId = (ctx as any)?.sessionKey ?? undefined;
|
|
1188
1259
|
if (sessionId) currentSessionId = sessionId;
|
|
@@ -1240,8 +1311,8 @@ const memoryPlugin = {
|
|
|
1240
1311
|
|
|
1241
1312
|
if (formattedMessages.length === 0) return;
|
|
1242
1313
|
|
|
1243
|
-
const addOpts = buildAddOptions(undefined, currentSessionId);
|
|
1244
|
-
const result = await
|
|
1314
|
+
const addOpts = buildAddOptions(effectiveCfg, undefined, currentSessionId);
|
|
1315
|
+
const result = await effectiveProvider.add(
|
|
1245
1316
|
formattedMessages,
|
|
1246
1317
|
addOpts,
|
|
1247
1318
|
);
|
package/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-memorylake-config
|
|
3
|
+
description: Use when the user asks to configure agent-specific memorylake properties (e.g. projectId) for the current agent. Writes to the agent-specific config file which overrides the global config.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent MemoryLake Config
|
|
7
|
+
|
|
8
|
+
Configure agent-specific memorylake properties for the current agent. The config file is located at `{workspace}/.memorylake/config.json`, where `{workspace}` is the agent's workspace directory. This config overrides corresponding properties from the global config.
|
|
9
|
+
|
|
10
|
+
## Step 1 — Confirm projectId
|
|
11
|
+
|
|
12
|
+
Ask the user for the `projectId` to configure.
|
|
13
|
+
|
|
14
|
+
If the user has already provided the `projectId` in their message, skip the question and proceed directly.
|
|
15
|
+
|
|
16
|
+
## Step 2 — Write Config
|
|
17
|
+
|
|
18
|
+
1. Ensure the `.memorylake/` directory exists inside the agent's workspace directory:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd {workspace}
|
|
22
|
+
mkdir -p .memorylake
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. If `.memorylake/config.json` already exists, read it first and merge the new properties into the existing config. Do NOT overwrite properties the user did not mention.
|
|
26
|
+
|
|
27
|
+
3. If `.memorylake/config.json` does not exist, create it with the provided properties.
|
|
28
|
+
|
|
29
|
+
4. Write the config file. Example format:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"projectId": "xxx"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Step 3 — Confirm Result
|
|
38
|
+
|
|
39
|
+
Read the written `.memorylake/config.json` and confirm to the user that the configuration is complete.
|
|
40
|
+
|
|
41
|
+
## Common Mistakes
|
|
42
|
+
|
|
43
|
+
- Do NOT overwrite existing properties that the user did not mention — always merge
|