@bamdra/bamdra-memory-vector 0.1.0 → 0.1.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/README.md +38 -3
- package/README.zh-CN.md +87 -0
- package/dist/index.js +128 -5
- package/openclaw.plugin.json +1 -0
- package/package.json +2 -1
- package/skills/bamdra-memory-vector-operator/SKILL.md +38 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# bamdra-memory-vector
|
|
2
2
|
|
|
3
|
-
`bamdra-memory-vector` is the
|
|
3
|
+
`bamdra-memory-vector` is the semantic retrieval plugin for the Bamdra OpenClaw suite.
|
|
4
4
|
|
|
5
5
|
It adds lightweight vector-style recall on top of `bamdra-openclaw-memory` without turning the whole stack into a heavy external vector-database deployment.
|
|
6
6
|
|
|
@@ -27,11 +27,46 @@ The repository currently looks small because the first public version is intenti
|
|
|
27
27
|
|
|
28
28
|
## Current Runtime Model
|
|
29
29
|
|
|
30
|
-
- Markdown root:
|
|
31
|
-
`~/.openclaw/memory/vector/markdown/`
|
|
30
|
+
- private Markdown root:
|
|
31
|
+
`~/.openclaw/memory/vector/markdown/private/`
|
|
32
|
+
- shared Markdown root:
|
|
33
|
+
`~/.openclaw/memory/vector/markdown/shared/`
|
|
32
34
|
- local index:
|
|
33
35
|
`~/.openclaw/memory/vector/index.json`
|
|
34
36
|
|
|
37
|
+
`markdownRoot` is still supported as a legacy base path, but the recommended production setup is to set `privateMarkdownRoot` and `sharedMarkdownRoot` explicitly.
|
|
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
|
+
|
|
47
|
+
## Best Practice: Obsidian-Friendly Layout
|
|
48
|
+
|
|
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`.
|
|
50
|
+
|
|
51
|
+
Recommended example:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"enabled": true,
|
|
56
|
+
"privateMarkdownRoot": "~/Documents/Obsidian/MyVault/openclaw/private",
|
|
57
|
+
"sharedMarkdownRoot": "~/Documents/Obsidian/MyVault/openclaw/shared",
|
|
58
|
+
"indexPath": "~/.openclaw/memory/vector/index.json",
|
|
59
|
+
"dimensions": 64
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This gives you:
|
|
64
|
+
|
|
65
|
+
- a private vault area for user-specific memory
|
|
66
|
+
- a shared vault area for team or household knowledge
|
|
67
|
+
- Markdown files that can be edited on any synced device
|
|
68
|
+
- a local index that OpenClaw can rebuild without exposing other users' data
|
|
69
|
+
|
|
35
70
|
This first version exposes a lightweight local vector-style index interface and keeps the integration surface ready for later LanceDB-backed evolution.
|
|
36
71
|
|
|
37
72
|
## Product Positioning
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# bamdra-memory-vector
|
|
2
|
+
|
|
3
|
+
`bamdra-memory-vector` 是 Bamdra OpenClaw 套件中的语义召回插件,而且它也可以独立运行。
|
|
4
|
+
|
|
5
|
+
## 它做什么
|
|
6
|
+
|
|
7
|
+
- 把记忆内容以 Markdown 可读形式写入本地
|
|
8
|
+
- 建立轻量的本地语义索引
|
|
9
|
+
- 按当前用户边界限制检索范围
|
|
10
|
+
- 为长程记忆回捞提供 Top-K 语义召回
|
|
11
|
+
- 保持私有记忆与共享知识分离
|
|
12
|
+
|
|
13
|
+
## 开源内容说明
|
|
14
|
+
|
|
15
|
+
当前开源版的实际源码已经包含在这个仓库里。
|
|
16
|
+
|
|
17
|
+
- 源码入口:
|
|
18
|
+
[src/index.ts](/Users/wood/workspace/macmini-openclaw/openclaw-enhanced/bamdra-memory-vector/src/index.ts)
|
|
19
|
+
- 插件清单:
|
|
20
|
+
[openclaw.plugin.json](/Users/wood/workspace/macmini-openclaw/openclaw-enhanced/bamdra-memory-vector/openclaw.plugin.json)
|
|
21
|
+
- 包元数据:
|
|
22
|
+
[package.json](/Users/wood/workspace/macmini-openclaw/openclaw-enhanced/bamdra-memory-vector/package.json)
|
|
23
|
+
|
|
24
|
+
仓库目前看起来比较轻,是因为首个公开版本刻意保持为紧凑、单入口插件。
|
|
25
|
+
|
|
26
|
+
## 当前运行模型
|
|
27
|
+
|
|
28
|
+
- 私有 Markdown 根目录:
|
|
29
|
+
`~/.openclaw/memory/vector/markdown/private/`
|
|
30
|
+
- 共享 Markdown 根目录:
|
|
31
|
+
`~/.openclaw/memory/vector/markdown/shared/`
|
|
32
|
+
- 本地索引:
|
|
33
|
+
`~/.openclaw/memory/vector/index.json`
|
|
34
|
+
|
|
35
|
+
这一版提供的是轻量本地向量式索引接口,同时为后续 LanceDB 演进保留接入面。
|
|
36
|
+
|
|
37
|
+
`markdownRoot` 仍然保留兼容,但更推荐在正式环境里显式设置 `privateMarkdownRoot` 和 `sharedMarkdownRoot`。
|
|
38
|
+
|
|
39
|
+
## 随包 Skill
|
|
40
|
+
|
|
41
|
+
这个包现在会附带独立的 operator skill:
|
|
42
|
+
|
|
43
|
+
- `bamdra-memory-vector-operator`
|
|
44
|
+
|
|
45
|
+
安装到 OpenClaw 后,bootstrap 可以把它复制到 `~/.openclaw/skills/` 并自动挂到 agent。
|
|
46
|
+
|
|
47
|
+
## 最佳实践:兼容 Obsidian 的双库布局
|
|
48
|
+
|
|
49
|
+
插件现在支持把私有记忆和共享知识分别落到两个 Markdown 根目录。这意味着你可以把 Markdown 指向 Obsidian 仓库、iCloud Drive、Git 同步目录或者 Syncthing 目录,而把本地索引仍然保留在 `~/.openclaw` 下面。
|
|
50
|
+
|
|
51
|
+
推荐配置示例:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"enabled": true,
|
|
56
|
+
"privateMarkdownRoot": "~/Documents/Obsidian/MyVault/openclaw/private",
|
|
57
|
+
"sharedMarkdownRoot": "~/Documents/Obsidian/MyVault/openclaw/shared",
|
|
58
|
+
"indexPath": "~/.openclaw/memory/vector/index.json",
|
|
59
|
+
"dimensions": 64
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
这样做的好处是:
|
|
64
|
+
|
|
65
|
+
- 私有记忆和共享知识库天然分开
|
|
66
|
+
- Markdown 文件可以在任何同步设备上直接编辑
|
|
67
|
+
- OpenClaw 继续使用本地索引,不依赖远端向量服务
|
|
68
|
+
- 更适合把 Obsidian 作为长期知识库编辑入口
|
|
69
|
+
|
|
70
|
+
## 产品定位
|
|
71
|
+
|
|
72
|
+
`bamdra-memory-vector` 不是用来替代 `bamdra-openclaw-memory` 的。
|
|
73
|
+
|
|
74
|
+
- `bamdra-openclaw-memory` 更强调连续性
|
|
75
|
+
- `bamdra-memory-vector` 更强调召回增强
|
|
76
|
+
|
|
77
|
+
两者组合后的价值是:
|
|
78
|
+
|
|
79
|
+
- 更强的长会话连续性
|
|
80
|
+
- Markdown 可读、可维护的记忆载体
|
|
81
|
+
- 不依赖重型基础设施的轻量语义召回
|
|
82
|
+
|
|
83
|
+
## 构建
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pnpm run bundle
|
|
87
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -24,18 +24,29 @@ __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 = "memory_vector_search";
|
|
32
42
|
var LocalVectorIndex = class {
|
|
33
43
|
config;
|
|
34
44
|
records = /* @__PURE__ */ new Map();
|
|
35
45
|
constructor(inputConfig) {
|
|
36
46
|
this.config = normalizeConfig(inputConfig);
|
|
37
47
|
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(this.config.indexPath), { recursive: true });
|
|
38
|
-
(0, import_node_fs.mkdirSync)(this.config.
|
|
48
|
+
(0, import_node_fs.mkdirSync)(this.config.privateMarkdownRoot, { recursive: true });
|
|
49
|
+
(0, import_node_fs.mkdirSync)(this.config.sharedMarkdownRoot, { recursive: true });
|
|
39
50
|
this.load();
|
|
40
51
|
}
|
|
41
52
|
upsert(args) {
|
|
@@ -54,7 +65,8 @@ ${args.text}`, this.config.dimensions),
|
|
|
54
65
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
55
66
|
};
|
|
56
67
|
this.records.set(id, record);
|
|
57
|
-
const
|
|
68
|
+
const markdownRoot = args.userId == null ? this.config.sharedMarkdownRoot : this.config.privateMarkdownRoot;
|
|
69
|
+
const markdownPath = (0, import_node_path.join)(markdownRoot, args.sourcePath);
|
|
58
70
|
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(markdownPath), { recursive: true });
|
|
59
71
|
(0, import_node_fs.writeFileSync)(markdownPath, `# ${args.title}
|
|
60
72
|
|
|
@@ -69,7 +81,7 @@ ${args.text}
|
|
|
69
81
|
if (args.userId == null) {
|
|
70
82
|
return record.userId == null;
|
|
71
83
|
}
|
|
72
|
-
return record.userId === args.userId;
|
|
84
|
+
return record.userId === args.userId || record.userId == null;
|
|
73
85
|
}).map((record) => ({
|
|
74
86
|
id: record.id,
|
|
75
87
|
userId: record.userId,
|
|
@@ -100,10 +112,16 @@ ${args.text}
|
|
|
100
112
|
}
|
|
101
113
|
};
|
|
102
114
|
function register(api) {
|
|
115
|
+
queueMicrotask(() => {
|
|
116
|
+
try {
|
|
117
|
+
bootstrapOpenClawHost();
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
});
|
|
103
121
|
const runtime = new LocalVectorIndex(api.pluginConfig ?? api.config ?? api.plugin?.config);
|
|
104
122
|
exposeVectorApi(runtime);
|
|
105
123
|
api.registerTool?.({
|
|
106
|
-
name:
|
|
124
|
+
name: TOOL_NAME,
|
|
107
125
|
description: "Search the current user's vector memory index",
|
|
108
126
|
parameters: {
|
|
109
127
|
type: "object",
|
|
@@ -148,13 +166,118 @@ function exposeVectorApi(runtime) {
|
|
|
148
166
|
}
|
|
149
167
|
function normalizeConfig(input) {
|
|
150
168
|
const root = (0, import_node_path.join)((0, import_node_os.homedir)(), ".openclaw", "memory", "vector");
|
|
169
|
+
const markdownRoot = input?.markdownRoot ?? (0, import_node_path.join)(root, "markdown");
|
|
151
170
|
return {
|
|
152
171
|
enabled: input?.enabled ?? true,
|
|
153
|
-
markdownRoot
|
|
172
|
+
markdownRoot,
|
|
173
|
+
privateMarkdownRoot: input?.privateMarkdownRoot ?? (0, import_node_path.join)(markdownRoot, "private"),
|
|
174
|
+
sharedMarkdownRoot: input?.sharedMarkdownRoot ?? (0, import_node_path.join)(markdownRoot, "shared"),
|
|
154
175
|
indexPath: input?.indexPath ?? (0, import_node_path.join)(root, "index.json"),
|
|
155
176
|
dimensions: input?.dimensions ?? 64
|
|
156
177
|
};
|
|
157
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
|
+
}
|
|
158
281
|
function embed(text, dimensions) {
|
|
159
282
|
const vector = Array.from({ length: dimensions }, () => 0);
|
|
160
283
|
const tokens = text.toLowerCase().split(/[^a-z0-9_\u4e00-\u9fff]+/i).filter(Boolean);
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamdra/bamdra-memory-vector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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"
|
|
@@ -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
|