@bamdra/bamdra-memory-vector 0.1.1 → 0.1.3

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 CHANGED
@@ -36,6 +36,14 @@ The repository currently looks small because the first public version is intenti
36
36
 
37
37
  `markdownRoot` is still supported as a legacy base path, but the recommended production setup is to set `privateMarkdownRoot` and `sharedMarkdownRoot` explicitly.
38
38
 
39
+ ## Bundled Skill
40
+
41
+ This package now ships its own standalone operator skill:
42
+
43
+ - `bamdra-memory-vector-operator`
44
+
45
+ When OpenClaw bootstraps the installed plugin, that skill can be materialized into `~/.openclaw/skills/` and attached automatically.
46
+
39
47
  ## Best Practice: Obsidian-Friendly Layout
40
48
 
41
49
  The plugin now supports separate roots for private memory and shared knowledge. That makes it practical to point Markdown storage at an Obsidian vault, iCloud Drive folder, Git-synced repo, or Syncthing workspace while keeping the local vector index in `~/.openclaw`.
package/README.zh-CN.md CHANGED
@@ -36,6 +36,14 @@
36
36
 
37
37
  `markdownRoot` 仍然保留兼容,但更推荐在正式环境里显式设置 `privateMarkdownRoot` 和 `sharedMarkdownRoot`。
38
38
 
39
+ ## 随包 Skill
40
+
41
+ 这个包现在会附带独立的 operator skill:
42
+
43
+ - `bamdra-memory-vector-operator`
44
+
45
+ 安装到 OpenClaw 后,bootstrap 可以把它复制到 `~/.openclaw/skills/` 并自动挂到 agent。
46
+
39
47
  ## 最佳实践:兼容 Obsidian 的双库布局
40
48
 
41
49
  插件现在支持把私有记忆和共享知识分别落到两个 Markdown 根目录。这意味着你可以把 Markdown 指向 Obsidian 仓库、iCloud Drive、Git 同步目录或者 Syncthing 目录,而把本地索引仍然保留在 `~/.openclaw` 下面。
package/dist/index.js CHANGED
@@ -24,11 +24,21 @@ __export(index_exports, {
24
24
  register: () => register
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
+
28
+ // ../node_modules/.pnpm/tsup@8.5.1_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
29
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
30
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
31
+
32
+ // src/index.ts
27
33
  var import_node_crypto = require("crypto");
28
34
  var import_node_fs = require("fs");
29
35
  var import_node_os = require("os");
30
36
  var import_node_path = require("path");
37
+ var import_node_url = require("url");
31
38
  var GLOBAL_VECTOR_API_KEY = "__OPENCLAW_BAMDRA_MEMORY_VECTOR__";
39
+ var PLUGIN_ID = "bamdra-memory-vector";
40
+ var SKILL_ID = "bamdra-memory-vector-operator";
41
+ var TOOL_NAME = "bamdra_memory_vector_search";
32
42
  var LocalVectorIndex = class {
33
43
  config;
34
44
  records = /* @__PURE__ */ new Map();
@@ -102,10 +112,16 @@ ${args.text}
102
112
  }
103
113
  };
104
114
  function register(api) {
115
+ queueMicrotask(() => {
116
+ try {
117
+ bootstrapOpenClawHost();
118
+ } catch {
119
+ }
120
+ });
105
121
  const runtime = new LocalVectorIndex(api.pluginConfig ?? api.config ?? api.plugin?.config);
106
122
  exposeVectorApi(runtime);
107
123
  api.registerTool?.({
108
- name: "memory_vector_search",
124
+ name: TOOL_NAME,
109
125
  description: "Search the current user's vector memory index",
110
126
  parameters: {
111
127
  type: "object",
@@ -160,6 +176,108 @@ function normalizeConfig(input) {
160
176
  dimensions: input?.dimensions ?? 64
161
177
  };
162
178
  }
179
+ function bootstrapOpenClawHost() {
180
+ const currentFile = (0, import_node_url.fileURLToPath)(importMetaUrl);
181
+ const runtimeDir = (0, import_node_path.dirname)(currentFile);
182
+ const packageRoot = (0, import_node_path.resolve)(runtimeDir, "..");
183
+ const openclawHome = (0, import_node_path.resolve)((0, import_node_os.homedir)(), ".openclaw");
184
+ const configPath = (0, import_node_path.join)(openclawHome, "openclaw.json");
185
+ const extensionRoot = (0, import_node_path.join)(openclawHome, "extensions");
186
+ const globalSkillsDir = (0, import_node_path.join)(openclawHome, "skills");
187
+ const skillSource = (0, import_node_path.join)(packageRoot, "skills", SKILL_ID);
188
+ const skillTarget = (0, import_node_path.join)(globalSkillsDir, SKILL_ID);
189
+ if (!runtimeDir.startsWith(extensionRoot) || !(0, import_node_fs.existsSync)(configPath)) {
190
+ return;
191
+ }
192
+ if ((0, import_node_fs.existsSync)(skillSource) && !(0, import_node_fs.existsSync)(skillTarget)) {
193
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(skillTarget), { recursive: true });
194
+ (0, import_node_fs.cpSync)(skillSource, skillTarget, { recursive: true });
195
+ }
196
+ const original = (0, import_node_fs.readFileSync)(configPath, "utf8");
197
+ const config = JSON.parse(original);
198
+ const changed = ensureHostConfig(config);
199
+ if (!changed) {
200
+ return;
201
+ }
202
+ (0, import_node_fs.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}
203
+ `, "utf8");
204
+ }
205
+ function ensureHostConfig(config) {
206
+ let changed = false;
207
+ const plugins = ensureObject(config, "plugins");
208
+ const entries = ensureObject(plugins, "entries");
209
+ const load = ensureObject(plugins, "load");
210
+ const tools = ensureObject(config, "tools");
211
+ const skills = ensureObject(config, "skills");
212
+ const skillsLoad = ensureObject(skills, "load");
213
+ const agents = ensureObject(config, "agents");
214
+ const entry = ensureObject(entries, PLUGIN_ID);
215
+ const entryConfig = ensureObject(entry, "config");
216
+ changed = ensureArrayIncludes(plugins, "allow", PLUGIN_ID) || changed;
217
+ changed = ensureArrayIncludes(load, "paths", (0, import_node_path.join)((0, import_node_os.homedir)(), ".openclaw", "extensions")) || changed;
218
+ changed = ensureArrayIncludes(skillsLoad, "extraDirs", (0, import_node_path.join)((0, import_node_os.homedir)(), ".openclaw", "skills")) || changed;
219
+ changed = ensureArrayIncludes(tools, "allow", TOOL_NAME) || changed;
220
+ if (entry.enabled !== false) {
221
+ entry.enabled = false;
222
+ changed = true;
223
+ }
224
+ if (entryConfig.enabled !== false) {
225
+ entryConfig.enabled = false;
226
+ changed = true;
227
+ }
228
+ if (typeof entryConfig.privateMarkdownRoot !== "string" || entryConfig.privateMarkdownRoot.length === 0) {
229
+ entryConfig.privateMarkdownRoot = "~/.openclaw/memory/vector/markdown/private";
230
+ changed = true;
231
+ }
232
+ if (typeof entryConfig.sharedMarkdownRoot !== "string" || entryConfig.sharedMarkdownRoot.length === 0) {
233
+ entryConfig.sharedMarkdownRoot = "~/.openclaw/memory/vector/markdown/shared";
234
+ changed = true;
235
+ }
236
+ if (typeof entryConfig.indexPath !== "string" || entryConfig.indexPath.length === 0) {
237
+ entryConfig.indexPath = "~/.openclaw/memory/vector/index.json";
238
+ changed = true;
239
+ }
240
+ changed = ensureAgentSkills(agents, SKILL_ID) || changed;
241
+ return changed;
242
+ }
243
+ function ensureObject(parent, key) {
244
+ const current = parent[key];
245
+ if (current && typeof current === "object" && !Array.isArray(current)) {
246
+ return current;
247
+ }
248
+ const next = {};
249
+ parent[key] = next;
250
+ return next;
251
+ }
252
+ function ensureArrayIncludes(parent, key, value) {
253
+ const current = Array.isArray(parent[key]) ? [...parent[key]] : [];
254
+ if (current.includes(value)) {
255
+ if (!Array.isArray(parent[key])) {
256
+ parent[key] = current;
257
+ }
258
+ return false;
259
+ }
260
+ current.push(value);
261
+ parent[key] = current;
262
+ return true;
263
+ }
264
+ function ensureAgentSkills(agents, skillId) {
265
+ const list = Array.isArray(agents.list) ? agents.list : [];
266
+ let changed = false;
267
+ for (const item of list) {
268
+ if (!item || typeof item !== "object") {
269
+ continue;
270
+ }
271
+ const agent = item;
272
+ const current = Array.isArray(agent.skills) ? [...agent.skills] : [];
273
+ if (!current.includes(skillId)) {
274
+ current.push(skillId);
275
+ agent.skills = current;
276
+ changed = true;
277
+ }
278
+ }
279
+ return changed;
280
+ }
163
281
  function embed(text, dimensions) {
164
282
  const vector = Array.from({ length: dimensions }, () => 0);
165
283
  const tokens = text.toLowerCase().split(/[^a-z0-9_\u4e00-\u9fff]+/i).filter(Boolean);
@@ -5,6 +5,7 @@
5
5
  "description": "Local vector-style semantic retrieval enhancement for Bamdra memory.",
6
6
  "version": "0.1.0",
7
7
  "main": "./dist/index.js",
8
+ "skills": ["./skills"],
8
9
  "configSchema": {
9
10
  "type": "object",
10
11
  "additionalProperties": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bamdra/bamdra-memory-vector",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Lightweight local semantic retrieval enhancement for the Bamdra OpenClaw memory suite.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.bamdra.com",
@@ -16,6 +16,7 @@
16
16
  "types": "./dist/index.d.ts",
17
17
  "files": [
18
18
  "dist",
19
+ "skills",
19
20
  "openclaw.plugin.json",
20
21
  "README.md",
21
22
  "LICENSE"
@@ -31,6 +32,10 @@
31
32
  "engines": {
32
33
  "node": ">=22"
33
34
  },
35
+ "scripts": {
36
+ "bundle": "node ../bamdra-openclaw-memory/scripts/run-local-bin.mjs tsup",
37
+ "prepublishOnly": "pnpm run bundle"
38
+ },
34
39
  "openclaw": {
35
40
  "id": "bamdra-memory-vector",
36
41
  "type": "tool",
@@ -51,8 +56,5 @@
51
56
  "dependencies": {},
52
57
  "devDependencies": {
53
58
  "@types/node": "^24.5.2"
54
- },
55
- "scripts": {
56
- "bundle": "node ../bamdra-openclaw-memory/scripts/run-local-bin.mjs tsup"
57
59
  }
58
- }
60
+ }
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: bamdra-memory-vector-operator
3
+ description: Use vector-enhanced Markdown recall to find semantically related private or shared knowledge without overfilling context.
4
+ ---
5
+
6
+ # Bamdra Memory Vector Operator
7
+
8
+ Treat `bamdra-memory-vector` as the semantic recall layer for memory and knowledge files.
9
+
10
+ It complements topic memory. Use it when the user remembers something fuzzily, when keyword matching may fail, or when Markdown knowledge files are the likely source.
11
+
12
+ ## Good Triggers
13
+
14
+ - “你还记得吗”
15
+ - “之前提过那个”
16
+ - “知识库里有没有”
17
+ - “我好像写过一份相关说明”
18
+ - the wording is approximate rather than exact
19
+
20
+ ## Retrieval Policy
21
+
22
+ - prefer vector recall when the user is referencing older or fuzzily remembered content
23
+ - use shared knowledge and private knowledge deliberately
24
+ - keep cross-user boundaries intact
25
+ - do not flood the prompt with low-signal chunks
26
+ - prefer a few strong recalls over many weak ones
27
+
28
+ ## Markdown Knowledge Model
29
+
30
+ - private Markdown is for one user's durable notes and memory fragments
31
+ - shared Markdown is for team or reusable knowledge
32
+ - both are editable by humans outside the runtime
33
+
34
+ ## Safety Rules
35
+
36
+ - never treat another user's private Markdown as searchable context
37
+ - do not reveal storage paths unless the user asks
38
+ - if retrieval confidence is weak, answer cautiously instead of pretending certainty