@co-engram/core 0.1.2 → 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/dist/bootstrap/classify.d.ts +67 -0
- package/dist/bootstrap/classify.d.ts.map +1 -0
- package/dist/bootstrap/classify.js +119 -0
- package/dist/bootstrap/classify.js.map +1 -0
- package/dist/bootstrap/index.d.ts +89 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +158 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/concepts/dictionary.d.ts +260 -0
- package/dist/concepts/dictionary.d.ts.map +1 -0
- package/dist/concepts/dictionary.js +253 -0
- package/dist/concepts/dictionary.js.map +1 -0
- package/dist/concepts/index.d.ts +11 -0
- package/dist/concepts/index.d.ts.map +1 -0
- package/dist/concepts/index.js +10 -0
- package/dist/concepts/index.js.map +1 -0
- package/dist/concepts/types.d.ts +65 -0
- package/dist/concepts/types.d.ts.map +1 -0
- package/dist/concepts/types.js +15 -0
- package/dist/concepts/types.js.map +1 -0
- package/dist/config/defaults.d.ts +32 -3
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +52 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +18 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +75 -3
- package/dist/config/index.js.map +1 -1
- package/dist/config/loader.d.ts +10 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +10 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +97 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/contradiction/auto-degrade.d.ts +11 -2
- package/dist/contradiction/auto-degrade.d.ts.map +1 -1
- package/dist/contradiction/auto-degrade.js +22 -0
- package/dist/contradiction/auto-degrade.js.map +1 -1
- package/dist/contradiction/resolver.d.ts.map +1 -1
- package/dist/contradiction/resolver.js +7 -1
- package/dist/contradiction/resolver.js.map +1 -1
- package/dist/dreaming/index.d.ts +1 -0
- package/dist/dreaming/index.d.ts.map +1 -1
- package/dist/dreaming/index.js +1 -0
- package/dist/dreaming/index.js.map +1 -1
- package/dist/dreaming/llm-pattern-abstraction.d.ts +31 -0
- package/dist/dreaming/llm-pattern-abstraction.d.ts.map +1 -0
- package/dist/dreaming/llm-pattern-abstraction.js +70 -0
- package/dist/dreaming/llm-pattern-abstraction.js.map +1 -0
- package/dist/dreaming/rem.d.ts.map +1 -1
- package/dist/dreaming/rem.js +1 -0
- package/dist/dreaming/rem.js.map +1 -1
- package/dist/dreaming/scheduler.d.ts +13 -0
- package/dist/dreaming/scheduler.d.ts.map +1 -1
- package/dist/dreaming/scheduler.js +14 -2
- package/dist/dreaming/scheduler.js.map +1 -1
- package/dist/evolution/triggered.d.ts.map +1 -1
- package/dist/evolution/triggered.js +1 -0
- package/dist/evolution/triggered.js.map +1 -1
- package/dist/generative/hypothesis.d.ts.map +1 -1
- package/dist/generative/hypothesis.js +1 -0
- package/dist/generative/hypothesis.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +1262 -32
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +34 -1
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +36 -7
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/zh.d.ts +698 -32
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +1267 -33
- package/dist/i18n/zh.js.map +1 -1
- package/dist/index/graph-builder.js +3 -3
- package/dist/index/graph-builder.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/learning/loop.d.ts +9 -0
- package/dist/learning/loop.d.ts.map +1 -1
- package/dist/learning/loop.js +42 -1
- package/dist/learning/loop.js.map +1 -1
- package/dist/maintenance/types.d.ts +11 -0
- package/dist/maintenance/types.d.ts.map +1 -1
- package/dist/maintenance/types.js.map +1 -1
- package/dist/merge/synapse-merger.js +6 -0
- package/dist/merge/synapse-merger.js.map +1 -1
- package/dist/merge-driver.cjs +64 -5
- package/dist/observability/audit-log.d.ts +7 -1
- package/dist/observability/audit-log.d.ts.map +1 -1
- package/dist/observability/audit-log.js.map +1 -1
- package/dist/observability/necessity-evaluator.d.ts +29 -0
- package/dist/observability/necessity-evaluator.d.ts.map +1 -1
- package/dist/observability/necessity-evaluator.js +240 -13
- package/dist/observability/necessity-evaluator.js.map +1 -1
- package/dist/observability/proposal-engine.d.ts.map +1 -1
- package/dist/observability/proposal-engine.js +12 -0
- package/dist/observability/proposal-engine.js.map +1 -1
- package/dist/observability/runtime-description-check.d.ts +55 -0
- package/dist/observability/runtime-description-check.d.ts.map +1 -0
- package/dist/observability/runtime-description-check.js +63 -0
- package/dist/observability/runtime-description-check.js.map +1 -0
- package/dist/prompt-signals/cache.d.ts +73 -0
- package/dist/prompt-signals/cache.d.ts.map +1 -1
- package/dist/prompt-signals/cache.js +102 -0
- package/dist/prompt-signals/cache.js.map +1 -1
- package/dist/prompt-signals/event-bus.d.ts +82 -0
- package/dist/prompt-signals/event-bus.d.ts.map +1 -0
- package/dist/prompt-signals/event-bus.js +105 -0
- package/dist/prompt-signals/event-bus.js.map +1 -0
- package/dist/prompt-signals/index.d.ts +2 -1
- package/dist/prompt-signals/index.d.ts.map +1 -1
- package/dist/prompt-signals/index.js +2 -1
- package/dist/prompt-signals/index.js.map +1 -1
- package/dist/reinforcement/ltp.d.ts +15 -1
- package/dist/reinforcement/ltp.d.ts.map +1 -1
- package/dist/reinforcement/ltp.js +24 -5
- package/dist/reinforcement/ltp.js.map +1 -1
- package/dist/reinforcement/related.d.ts +31 -2
- package/dist/reinforcement/related.d.ts.map +1 -1
- package/dist/reinforcement/related.js +39 -3
- package/dist/reinforcement/related.js.map +1 -1
- package/dist/retrieval/filter.d.ts.map +1 -1
- package/dist/retrieval/filter.js +7 -0
- package/dist/retrieval/filter.js.map +1 -1
- package/dist/retrieval/fts.d.ts +6 -5
- package/dist/retrieval/fts.d.ts.map +1 -1
- package/dist/retrieval/fts.js +74 -22
- package/dist/retrieval/fts.js.map +1 -1
- package/dist/status/index.d.ts +7 -0
- package/dist/status/index.d.ts.map +1 -0
- package/dist/status/index.js +7 -0
- package/dist/status/index.js.map +1 -0
- package/dist/status/status.d.ts +132 -0
- package/dist/status/status.d.ts.map +1 -0
- package/dist/status/status.js +437 -0
- package/dist/status/status.js.map +1 -0
- package/dist/storage/engram-store.d.ts.map +1 -1
- package/dist/storage/engram-store.js +17 -2
- package/dist/storage/engram-store.js.map +1 -1
- package/dist/storage/git.d.ts +168 -0
- package/dist/storage/git.d.ts.map +1 -1
- package/dist/storage/git.js +616 -33
- package/dist/storage/git.js.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/infra-doctor.d.ts +42 -0
- package/dist/storage/infra-doctor.d.ts.map +1 -0
- package/dist/storage/infra-doctor.js +92 -0
- package/dist/storage/infra-doctor.js.map +1 -0
- package/dist/storage/obsidian-links.d.ts +73 -0
- package/dist/storage/obsidian-links.d.ts.map +1 -0
- package/dist/storage/obsidian-links.js +177 -0
- package/dist/storage/obsidian-links.js.map +1 -0
- package/dist/storage/path.d.ts +24 -0
- package/dist/storage/path.d.ts.map +1 -1
- package/dist/storage/path.js +53 -0
- package/dist/storage/path.js.map +1 -1
- package/dist/storage/repository.d.ts +37 -1
- package/dist/storage/repository.d.ts.map +1 -1
- package/dist/storage/repository.js +235 -17
- package/dist/storage/repository.js.map +1 -1
- package/dist/storage/synapse-store.d.ts +7 -1
- package/dist/storage/synapse-store.d.ts.map +1 -1
- package/dist/storage/synapse-store.js +8 -0
- package/dist/storage/synapse-store.js.map +1 -1
- package/dist/tools/audit-query-tool.d.ts +53 -0
- package/dist/tools/audit-query-tool.d.ts.map +1 -0
- package/dist/tools/audit-query-tool.js +123 -0
- package/dist/tools/audit-query-tool.js.map +1 -0
- package/dist/tools/doctor-tools.d.ts +5 -0
- package/dist/tools/doctor-tools.d.ts.map +1 -1
- package/dist/tools/doctor-tools.js +11 -3
- package/dist/tools/doctor-tools.js.map +1 -1
- package/dist/tools/engram-tools.d.ts +13 -0
- package/dist/tools/engram-tools.d.ts.map +1 -1
- package/dist/tools/engram-tools.js +72 -8
- package/dist/tools/engram-tools.js.map +1 -1
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/llm-descriptions.d.ts +28 -28
- package/dist/tools/llm-descriptions.d.ts.map +1 -1
- package/dist/tools/llm-descriptions.js +56 -489
- package/dist/tools/llm-descriptions.js.map +1 -1
- package/dist/tools/normalization.d.ts +43 -0
- package/dist/tools/normalization.d.ts.map +1 -0
- package/dist/tools/normalization.js +68 -0
- package/dist/tools/normalization.js.map +1 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +6 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/schemas.d.ts +67 -11
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +61 -6
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/skill-tools.js +1 -1
- package/dist/tools/skill-tools.js.map +1 -1
- package/dist/tools/synapse-tools.d.ts.map +1 -1
- package/dist/tools/synapse-tools.js +1 -0
- package/dist/tools/synapse-tools.js.map +1 -1
- package/dist/tools/sync-tools.d.ts +102 -0
- package/dist/tools/sync-tools.d.ts.map +1 -0
- package/dist/tools/sync-tools.js +309 -0
- package/dist/tools/sync-tools.js.map +1 -0
- package/dist/tools/synthesize-tools.d.ts +79 -0
- package/dist/tools/synthesize-tools.d.ts.map +1 -0
- package/dist/tools/synthesize-tools.js +297 -0
- package/dist/tools/synthesize-tools.js.map +1 -0
- package/dist/tools/tool-profile.d.ts +68 -0
- package/dist/tools/tool-profile.d.ts.map +1 -0
- package/dist/tools/tool-profile.js +174 -0
- package/dist/tools/tool-profile.js.map +1 -0
- package/dist/tools/tool.d.ts +17 -0
- package/dist/tools/tool.d.ts.map +1 -1
- package/dist/tools/tool.js.map +1 -1
- package/dist/types/disclosure.d.ts +7 -0
- package/dist/types/disclosure.d.ts.map +1 -1
- package/dist/types/repository-types.d.ts +17 -1
- package/dist/types/repository-types.d.ts.map +1 -1
- package/dist/types/synapse.d.ts +19 -1
- package/dist/types/synapse.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -90,6 +90,18 @@ export declare class EngramRepository {
|
|
|
90
90
|
private updateIndexEntry;
|
|
91
91
|
/** 从 index 删除一条并写盘 */
|
|
92
92
|
private deleteIndexEntry;
|
|
93
|
+
/**
|
|
94
|
+
* 清理索引中所有指向 relativePath 的孤儿 entry,返回清理数量。
|
|
95
|
+
*
|
|
96
|
+
* 触发场景:外部(用户 rm / git 操作 / 进程异常)删除 engram 文件后,
|
|
97
|
+
* engram-index.json 仍保留旧 ULID 的 entry。下一次 createEngram 写入
|
|
98
|
+
* 同 path 的新 ULID 会留下永不消失的孤儿,导致 listEngrams / viewer
|
|
99
|
+
* 显示"重影"(同一文件被两个 ULID 引用)。
|
|
100
|
+
*
|
|
101
|
+
* 同 path 出现多条 entry 本身就是不变量破坏 —— 此处发现即清。多数调用
|
|
102
|
+
* 不会命中,N=0 时是 O(|index|) 扫一次,可接受。
|
|
103
|
+
*/
|
|
104
|
+
private purgeStaleIndexEntriesForPath;
|
|
93
105
|
/**
|
|
94
106
|
* 启动对 engram-index.json 的 fs.watch 监听。
|
|
95
107
|
*
|
|
@@ -134,7 +146,13 @@ export declare class EngramRepository {
|
|
|
134
146
|
createEngram(input: EngramCreateInput & {
|
|
135
147
|
pathHint?: string;
|
|
136
148
|
}): Engram;
|
|
137
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* 默认路径:{domainTags.join('/')/}{slug}.md
|
|
151
|
+
*
|
|
152
|
+
* private engram 自动落 `private/` 子目录(被 .gitignore 隔离出团队仓库)。
|
|
153
|
+
* 注意:本函数只用于「无 pathHint」场景;调用方传 pathHint 时直接尊重用户路径,
|
|
154
|
+
* 不会自动加 private 前缀(避免 `private/private/...` 双前缀)。
|
|
155
|
+
*/
|
|
138
156
|
private deriveDefaultPath;
|
|
139
157
|
/**
|
|
140
158
|
* 读取完整 Engram(单文件 + 统计)
|
|
@@ -154,8 +172,20 @@ export declare class EngramRepository {
|
|
|
154
172
|
updateEngram(stableId: string, input: EngramUpdateInput): Engram;
|
|
155
173
|
/** 重新组装路径:替换 basename 为 newSlug */
|
|
156
174
|
private rebuildPath;
|
|
175
|
+
/**
|
|
176
|
+
* 调整路径的 visibility 前缀(不改动 basename):
|
|
177
|
+
* - `'private'` → 加 `private/` 前缀(若已含则幂等返回原值)
|
|
178
|
+
* - 其他(`'public'`/`'team'`/`'restricted'`) → 移除 `private/` 前缀(若不含则幂等返回)
|
|
179
|
+
*
|
|
180
|
+
* 用于 updateEngram 中 visibility 变更时的路径迁移。
|
|
181
|
+
* 注意:仅处理前缀,basename 由 rebuildPath 负责,两者正交可串联应用。
|
|
182
|
+
*/
|
|
183
|
+
private rebuildPathForVisibility;
|
|
157
184
|
/**
|
|
158
185
|
* 删除 Engram + 级联删除触及的 synapses + 清理 index
|
|
186
|
+
*
|
|
187
|
+
* 走 `this.deleteSynapsesTouching` 方法版而非模块函数,以触发
|
|
188
|
+
* 邻居派生段( Obsidian wikilinks)的 cascade refresh。
|
|
159
189
|
*/
|
|
160
190
|
deleteEngram(stableId: string): void;
|
|
161
191
|
/**
|
|
@@ -205,6 +235,12 @@ export declare class EngramRepository {
|
|
|
205
235
|
readSynapseByEndpoints(from: string, to: string, kind: SynapseKind): Synapse | undefined;
|
|
206
236
|
/** 按 id 查 synapse */
|
|
207
237
|
readSynapseById(synapseId: string): Synapse | undefined;
|
|
238
|
+
/**
|
|
239
|
+
* 触发 Obsidian 派生段重建(多条 engram 一次性刷新,去重)。
|
|
240
|
+
*
|
|
241
|
+
* 永不抛(regenerateObsidianLinks 内部已吞错)。调用方无需 try/catch。
|
|
242
|
+
*/
|
|
243
|
+
private refreshObsidianLinks;
|
|
208
244
|
/** 创建 synapse(idempotent) */
|
|
209
245
|
createSynapse(input: SynapseCreateInput): Synapse;
|
|
210
246
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/storage/repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,eAAe,EAGf,YAAY,EACZ,iBAAiB,EAGjB,gBAAgB,EAChB,kBAAkB,EAClB,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAEV,gBAAgB,EAChB,YAAY,EAEZ,YAAY,EACb,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/storage/repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,11 +19,14 @@ import { join, relative, sep } from "node:path";
|
|
|
19
19
|
import { ulid } from "ulid";
|
|
20
20
|
import { isStableEngramId } from "../types/repository-types.js";
|
|
21
21
|
import { slugify, inferDomainTagsFromPath } from "../types/slugify.js";
|
|
22
|
+
import { safeEmit } from "../prompt-signals/event-bus.js";
|
|
23
|
+
import { safeJoinWithinRoot, isPathWithinRoot, } from "./path.js";
|
|
22
24
|
import { computeContentHash, computeContentSize } from "./hash.js";
|
|
23
25
|
import { DEFAULT_LANGUAGE } from "../i18n/index.js";
|
|
24
26
|
import { readEngramFile, writeEngramFile, deleteEngramFile, renameEngramFile, parseEngramFile, detectEngramFileLanguage, } from "./engram-store.js";
|
|
25
27
|
import { SYNAPSES_DIR, collectAllSynapses, upsertSynapse, readSynapseByEndpoints, readSynapseById, listSynapsesForEngram, deleteSynapsesTouching, synapseRelativePath, deleteSynapseFile, writeSynapseFile, parseSynapseFile, } from "./synapse-store.js";
|
|
26
28
|
import { buildIndexEntryFromFrontmatter, engramIndexPath, readEngramIndex, rebuildEngramIndex, removeEngramIndexEntry, upsertEngramIndexEntry, writeEngramIndex, } from "./engram-index.js";
|
|
29
|
+
import { regenerateObsidianLinks, checkObsidianView, } from "./obsidian-links.js";
|
|
27
30
|
const DEFAULT_IMPORTANCE = 0.5;
|
|
28
31
|
const DEFAULT_CONFIDENCE_BY_SOURCE = {
|
|
29
32
|
firsthand: 0.85,
|
|
@@ -64,6 +67,21 @@ function deriveAutoSummary(content, title) {
|
|
|
64
67
|
return cleaned;
|
|
65
68
|
return cleaned.slice(0, 197) + "...";
|
|
66
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Synapse visibility 继承规则:取两端 engram 的最严。
|
|
72
|
+
*
|
|
73
|
+
* 严格度排序:`private` > `restricted` > `team` > `public`。
|
|
74
|
+
* 任一端是 private,synapse 整条就按 private 处理(保守策略)。
|
|
75
|
+
*/
|
|
76
|
+
const VIS_STRICTNESS = {
|
|
77
|
+
public: 0,
|
|
78
|
+
team: 1,
|
|
79
|
+
restricted: 2,
|
|
80
|
+
private: 3,
|
|
81
|
+
};
|
|
82
|
+
function maxVisibility(a, b) {
|
|
83
|
+
return VIS_STRICTNESS[a] >= VIS_STRICTNESS[b] ? a : b;
|
|
84
|
+
}
|
|
67
85
|
/**
|
|
68
86
|
* EngramRepository — per-edge synapse + ULID stable id + 单文件 engram
|
|
69
87
|
*
|
|
@@ -180,6 +198,29 @@ export class EngramRepository {
|
|
|
180
198
|
removeEngramIndexEntry(index, id);
|
|
181
199
|
this.persistIndex(index);
|
|
182
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* 清理索引中所有指向 relativePath 的孤儿 entry,返回清理数量。
|
|
203
|
+
*
|
|
204
|
+
* 触发场景:外部(用户 rm / git 操作 / 进程异常)删除 engram 文件后,
|
|
205
|
+
* engram-index.json 仍保留旧 ULID 的 entry。下一次 createEngram 写入
|
|
206
|
+
* 同 path 的新 ULID 会留下永不消失的孤儿,导致 listEngrams / viewer
|
|
207
|
+
* 显示"重影"(同一文件被两个 ULID 引用)。
|
|
208
|
+
*
|
|
209
|
+
* 同 path 出现多条 entry 本身就是不变量破坏 —— 此处发现即清。多数调用
|
|
210
|
+
* 不会命中,N=0 时是 O(|index|) 扫一次,可接受。
|
|
211
|
+
*/
|
|
212
|
+
purgeStaleIndexEntriesForPath(relativePath) {
|
|
213
|
+
const index = this.getIndex();
|
|
214
|
+
const stale = [];
|
|
215
|
+
for (const [id, entry] of index.entries) {
|
|
216
|
+
if (entry.path === relativePath)
|
|
217
|
+
stale.push(id);
|
|
218
|
+
}
|
|
219
|
+
for (const id of stale) {
|
|
220
|
+
this.deleteIndexEntry(id);
|
|
221
|
+
}
|
|
222
|
+
return stale.length;
|
|
223
|
+
}
|
|
183
224
|
// ─── Cross-process watcher ─────────────────────────────────────────────
|
|
184
225
|
/**
|
|
185
226
|
* 启动对 engram-index.json 的 fs.watch 监听。
|
|
@@ -262,17 +303,26 @@ export class EngramRepository {
|
|
|
262
303
|
/** 解析 stableId → 相对路径 */
|
|
263
304
|
resolvePath(stableId) {
|
|
264
305
|
if (!isStableEngramId(stableId)) {
|
|
265
|
-
// 兼容:可能是相对路径,直接当 path
|
|
306
|
+
// 兼容:可能是相对路径,直接当 path 用。
|
|
307
|
+
// 但必须先校验路径在 root 内(防 `..` 逃逸,path traversal 防御)
|
|
308
|
+
if (!isPathWithinRoot(this.config.rootPath, stableId))
|
|
309
|
+
return undefined;
|
|
266
310
|
if (this.existsAtPath(stableId))
|
|
267
311
|
return stableId;
|
|
268
312
|
return undefined;
|
|
269
313
|
}
|
|
270
314
|
const entry = this.getIndex().entries.get(stableId);
|
|
315
|
+
// 防御:索引中的 path 也校验(理论上是 trusted,但 doctor 自愈后可能含异常)
|
|
316
|
+
if (entry?.path && !isPathWithinRoot(this.config.rootPath, entry.path)) {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
271
319
|
return entry?.path;
|
|
272
320
|
}
|
|
273
321
|
/** 检查相对路径是否存在 engram 文件 */
|
|
274
322
|
existsAtPath(relativePath) {
|
|
275
|
-
|
|
323
|
+
if (!isPathWithinRoot(this.config.rootPath, relativePath))
|
|
324
|
+
return false;
|
|
325
|
+
return existsSync(safeJoinWithinRoot(this.config.rootPath, relativePath));
|
|
276
326
|
}
|
|
277
327
|
// ─── Engram CRUD ───────────────────────────────────────────────────────
|
|
278
328
|
/**
|
|
@@ -290,11 +340,18 @@ export class EngramRepository {
|
|
|
290
340
|
const sourceType = input.sourceType ?? "firsthand";
|
|
291
341
|
const contentHash = computeContentHash(input.content);
|
|
292
342
|
const contentSize = computeContentSize(input.content);
|
|
343
|
+
// pathHint 优先;否则用 deriveDefaultPath(slugify title + raw domainTags + .md)
|
|
344
|
+
// safeJoinWithinRoot 拦截 `..` 逃逸与绝对路径(path traversal 防御)
|
|
293
345
|
const relativePath = input.pathHint ?? this.deriveDefaultPath(input);
|
|
294
|
-
const absolutePath =
|
|
346
|
+
const absolutePath = safeJoinWithinRoot(this.config.rootPath, relativePath);
|
|
295
347
|
if (existsSync(absolutePath)) {
|
|
296
348
|
throw new Error(`Engram already exists at ${relativePath}`);
|
|
297
349
|
}
|
|
350
|
+
// 自愈:外部 rm / git 操作可能让 engram-index.json 残留指向同 path
|
|
351
|
+
// 但磁盘已无文件的孤儿 entry。新 ULID 写入后会留下永不消失的"重影"
|
|
352
|
+
// (listEngrams / viewer 显示重复节点)。在写入前清理。engram_doctor
|
|
353
|
+
// 是手动巡检版本,此处是写入路径的自动防线。
|
|
354
|
+
this.purgeStaleIndexEntriesForPath(relativePath);
|
|
298
355
|
const hasExplicitDomainTags = input.domainTags.length > 0;
|
|
299
356
|
const frontmatter = {
|
|
300
357
|
id: stableId,
|
|
@@ -344,14 +401,29 @@ export class EngramRepository {
|
|
|
344
401
|
contentHash,
|
|
345
402
|
});
|
|
346
403
|
this.updateIndexEntry(entry);
|
|
404
|
+
// Task 3.4 Phase B:engram 创建后 emit,让 prompt-signals cache 失效并 debounced rebuild
|
|
405
|
+
safeEmit({
|
|
406
|
+
type: "engram_created",
|
|
407
|
+
engramId: stableId,
|
|
408
|
+
at: new Date().toISOString(),
|
|
409
|
+
});
|
|
347
410
|
return this.readEngram(stableId);
|
|
348
411
|
}
|
|
349
|
-
/**
|
|
412
|
+
/**
|
|
413
|
+
* 默认路径:{domainTags.join('/')/}{slug}.md
|
|
414
|
+
*
|
|
415
|
+
* private engram 自动落 `private/` 子目录(被 .gitignore 隔离出团队仓库)。
|
|
416
|
+
* 注意:本函数只用于「无 pathHint」场景;调用方传 pathHint 时直接尊重用户路径,
|
|
417
|
+
* 不会自动加 private 前缀(避免 `private/private/...` 双前缀)。
|
|
418
|
+
*/
|
|
350
419
|
deriveDefaultPath(input) {
|
|
351
420
|
const slug = slugify(input.title);
|
|
352
421
|
const domains = input.domainTags.length > 0 ? input.domainTags : [];
|
|
353
422
|
const parts = [...domains, `${slug}.md`];
|
|
354
|
-
|
|
423
|
+
const basePath = parts.join("/");
|
|
424
|
+
return input.visibility === "private"
|
|
425
|
+
? `private/${basePath}`
|
|
426
|
+
: basePath;
|
|
355
427
|
}
|
|
356
428
|
/**
|
|
357
429
|
* 读取完整 Engram(单文件 + 统计)
|
|
@@ -361,7 +433,8 @@ export class EngramRepository {
|
|
|
361
433
|
if (!relativePath) {
|
|
362
434
|
throw new Error(`Engram not found: ${stableId}`);
|
|
363
435
|
}
|
|
364
|
-
|
|
436
|
+
// resolvePath 已校验路径在 root 内,这里再防御一次
|
|
437
|
+
const absolutePath = safeJoinWithinRoot(this.config.rootPath, relativePath);
|
|
365
438
|
const file = readEngramFile(absolutePath);
|
|
366
439
|
return this.assembleEngram(file, relativePath);
|
|
367
440
|
}
|
|
@@ -371,7 +444,8 @@ export class EngramRepository {
|
|
|
371
444
|
exists(stableId) {
|
|
372
445
|
const relativePath = this.resolvePath(stableId);
|
|
373
446
|
return (relativePath !== undefined &&
|
|
374
|
-
|
|
447
|
+
isPathWithinRoot(this.config.rootPath, relativePath) &&
|
|
448
|
+
existsSync(safeJoinWithinRoot(this.config.rootPath, relativePath)));
|
|
375
449
|
}
|
|
376
450
|
/**
|
|
377
451
|
* 更新 Engram(content/frontmatter 双写)
|
|
@@ -454,9 +528,18 @@ export class EngramRepository {
|
|
|
454
528
|
const newSlugUnlocked = oldFrontmatter.slug === undefined
|
|
455
529
|
? slugify(newTitle)
|
|
456
530
|
: oldFrontmatter.slug;
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
531
|
+
// 处理 visibility 变化:public/team/restricted ↔ private → 路径前缀调整
|
|
532
|
+
// (private engram 落 `private/` 子目录,变更 visibility 时同步迁移路径)
|
|
533
|
+
const oldVisibility = oldFrontmatter.visibility ?? "public";
|
|
534
|
+
const visibilityChanged = newVisibility !== oldVisibility;
|
|
535
|
+
// slug + visibility 都可能触发 rename,正交串联应用
|
|
536
|
+
let newPath = relativePath;
|
|
537
|
+
if (newSlugUnlocked !== oldSlug) {
|
|
538
|
+
newPath = this.rebuildPath(newPath, newSlugUnlocked);
|
|
539
|
+
}
|
|
540
|
+
if (visibilityChanged) {
|
|
541
|
+
newPath = this.rebuildPathForVisibility(newPath, newVisibility);
|
|
542
|
+
}
|
|
460
543
|
const newFile = {
|
|
461
544
|
frontmatter: newFrontmatter,
|
|
462
545
|
content: newContent,
|
|
@@ -464,10 +547,15 @@ export class EngramRepository {
|
|
|
464
547
|
if (newPath !== relativePath) {
|
|
465
548
|
const newAbsolutePath = join(this.config.rootPath, newPath);
|
|
466
549
|
if (existsSync(newAbsolutePath)) {
|
|
467
|
-
|
|
550
|
+
// 原子性保证:目标已存在就报错,不动旧文件
|
|
551
|
+
throw new Error(`Rename conflict: ${newPath} already exists`);
|
|
468
552
|
}
|
|
469
553
|
writeEngramFile(newAbsolutePath, newFile, this.language);
|
|
470
554
|
rmSync(absolutePath);
|
|
555
|
+
// 旧路径孤儿 index entry 清理(原 updateEngram 漏了这一步):
|
|
556
|
+
// rename 后旧 path 的反向 entry(path → stableId)若不清理,会留下
|
|
557
|
+
// 「磁盘无文件但 index 有记录」的孤儿,listEngrams / viewer 会显示重影。
|
|
558
|
+
this.purgeStaleIndexEntriesForPath(relativePath);
|
|
471
559
|
}
|
|
472
560
|
else {
|
|
473
561
|
writeEngramFile(absolutePath, newFile, this.language);
|
|
@@ -488,8 +576,28 @@ export class EngramRepository {
|
|
|
488
576
|
parts[parts.length - 1] = `${newSlug}.md`;
|
|
489
577
|
return parts.join("/");
|
|
490
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* 调整路径的 visibility 前缀(不改动 basename):
|
|
581
|
+
* - `'private'` → 加 `private/` 前缀(若已含则幂等返回原值)
|
|
582
|
+
* - 其他(`'public'`/`'team'`/`'restricted'`) → 移除 `private/` 前缀(若不含则幂等返回)
|
|
583
|
+
*
|
|
584
|
+
* 用于 updateEngram 中 visibility 变更时的路径迁移。
|
|
585
|
+
* 注意:仅处理前缀,basename 由 rebuildPath 负责,两者正交可串联应用。
|
|
586
|
+
*/
|
|
587
|
+
rebuildPathForVisibility(path, visibility) {
|
|
588
|
+
const PRIVATE_PREFIX = "private/";
|
|
589
|
+
if (visibility === "private") {
|
|
590
|
+
return path.startsWith(PRIVATE_PREFIX) ? path : `${PRIVATE_PREFIX}${path}`;
|
|
591
|
+
}
|
|
592
|
+
return path.startsWith(PRIVATE_PREFIX)
|
|
593
|
+
? path.slice(PRIVATE_PREFIX.length)
|
|
594
|
+
: path;
|
|
595
|
+
}
|
|
491
596
|
/**
|
|
492
597
|
* 删除 Engram + 级联删除触及的 synapses + 清理 index
|
|
598
|
+
*
|
|
599
|
+
* 走 `this.deleteSynapsesTouching` 方法版而非模块函数,以触发
|
|
600
|
+
* 邻居派生段( Obsidian wikilinks)的 cascade refresh。
|
|
493
601
|
*/
|
|
494
602
|
deleteEngram(stableId) {
|
|
495
603
|
const relativePath = this.resolvePath(stableId);
|
|
@@ -497,7 +605,7 @@ export class EngramRepository {
|
|
|
497
605
|
return;
|
|
498
606
|
const absolutePath = join(this.config.rootPath, relativePath);
|
|
499
607
|
deleteEngramFile(absolutePath);
|
|
500
|
-
deleteSynapsesTouching(
|
|
608
|
+
this.deleteSynapsesTouching(stableId);
|
|
501
609
|
if (isStableEngramId(stableId)) {
|
|
502
610
|
this.deleteIndexEntry(stableId);
|
|
503
611
|
}
|
|
@@ -613,9 +721,40 @@ export class EngramRepository {
|
|
|
613
721
|
readSynapseById(synapseId) {
|
|
614
722
|
return readSynapseById(this.config.rootPath, synapseId);
|
|
615
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* 触发 Obsidian 派生段重建(多条 engram 一次性刷新,去重)。
|
|
726
|
+
*
|
|
727
|
+
* 永不抛(regenerateObsidianLinks 内部已吞错)。调用方无需 try/catch。
|
|
728
|
+
*/
|
|
729
|
+
refreshObsidianLinks(...ids) {
|
|
730
|
+
const seen = new Set();
|
|
731
|
+
for (const id of ids) {
|
|
732
|
+
if (!id || seen.has(id))
|
|
733
|
+
continue;
|
|
734
|
+
seen.add(id);
|
|
735
|
+
regenerateObsidianLinks(this.config.rootPath, id, this.language);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
616
738
|
/** 创建 synapse(idempotent) */
|
|
617
739
|
createSynapse(input) {
|
|
618
|
-
|
|
740
|
+
// 继承最严 visibility:取 from/to 两端 engram 的 max(private > restricted > team > public)
|
|
741
|
+
// 端点查不到(已删/无效 id)时降级 'public',不阻塞 synapse 创建
|
|
742
|
+
let fromVis = "public";
|
|
743
|
+
let toVis = "public";
|
|
744
|
+
try {
|
|
745
|
+
fromVis = this.readEngram(input.from).visibility ?? "public";
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
// from engram 不存在或不可读,降级 public
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
toVis = this.readEngram(input.to).visibility ?? "public";
|
|
752
|
+
}
|
|
753
|
+
catch {
|
|
754
|
+
// to engram 不存在或不可读,降级 public
|
|
755
|
+
}
|
|
756
|
+
const inheritedVisibility = maxVisibility(fromVis, toVis);
|
|
757
|
+
const result = upsertSynapse(this.config.rootPath, {
|
|
619
758
|
from: input.from,
|
|
620
759
|
to: input.to,
|
|
621
760
|
kind: input.kind,
|
|
@@ -625,8 +764,17 @@ export class EngramRepository {
|
|
|
625
764
|
createdBy: input.createdBy,
|
|
626
765
|
sourceSemantic: input.sourceSemantic,
|
|
627
766
|
targetSemantic: input.targetSemantic,
|
|
767
|
+
visibility: inheritedVisibility,
|
|
628
768
|
language: this.language,
|
|
629
769
|
});
|
|
770
|
+
this.refreshObsidianLinks(input.from, input.to);
|
|
771
|
+
// Task 3.4 Phase B:synapse 创建影响 graph 结构,触发 prompt-signals rebuild
|
|
772
|
+
safeEmit({
|
|
773
|
+
type: "synapse_created",
|
|
774
|
+
engramId: input.from,
|
|
775
|
+
at: new Date().toISOString(),
|
|
776
|
+
});
|
|
777
|
+
return result;
|
|
630
778
|
}
|
|
631
779
|
/**
|
|
632
780
|
* 更新 synapse(走 upsert,合并 evidence)。
|
|
@@ -648,7 +796,7 @@ export class EngramRepository {
|
|
|
648
796
|
const oldPath = join(this.config.rootPath, synapseRelativePath(target.id, target.kind));
|
|
649
797
|
deleteSynapseFile(oldPath);
|
|
650
798
|
}
|
|
651
|
-
|
|
799
|
+
const result = upsertSynapse(this.config.rootPath, {
|
|
652
800
|
from: target.from,
|
|
653
801
|
to: target.to,
|
|
654
802
|
kind: nextKind,
|
|
@@ -659,8 +807,13 @@ export class EngramRepository {
|
|
|
659
807
|
sourceSemantic: target.sourceSemantic,
|
|
660
808
|
targetSemantic: target.targetSemantic,
|
|
661
809
|
resolutionState: target.resolutionState,
|
|
810
|
+
// 保守策略:不因端点 visibility 提升而自动调整 synapse visibility,
|
|
811
|
+
// 保留原值。Phase 1.5 可加 recomputeSynapseVisibility(engramId)。
|
|
812
|
+
visibility: target.visibility ?? "public",
|
|
662
813
|
language: this.language,
|
|
663
814
|
});
|
|
815
|
+
this.refreshObsidianLinks(target.from, target.to);
|
|
816
|
+
return result;
|
|
664
817
|
}
|
|
665
818
|
/** 删除某条 synapse */
|
|
666
819
|
deleteSynapse(synapseId) {
|
|
@@ -669,10 +822,26 @@ export class EngramRepository {
|
|
|
669
822
|
return;
|
|
670
823
|
const path = join(this.config.rootPath, synapseRelativePath(syn.id, syn.kind));
|
|
671
824
|
deleteSynapseFile(path);
|
|
825
|
+
this.refreshObsidianLinks(syn.from, syn.to);
|
|
672
826
|
}
|
|
673
827
|
/** 级联删除触及 engram 的所有 synapse */
|
|
674
828
|
deleteSynapsesTouching(engramId) {
|
|
675
|
-
|
|
829
|
+
// 先抓所有邻居 endpoint — 删除后这些 synapse 就找不到了,
|
|
830
|
+
// 邻居 engram.md 的派生段还引用着 engramId,需要重建。
|
|
831
|
+
const touching = listSynapsesForEngram(this.config.rootPath, engramId);
|
|
832
|
+
const neighbors = new Set();
|
|
833
|
+
for (const s of touching.outgoing) {
|
|
834
|
+
if (s.to !== engramId)
|
|
835
|
+
neighbors.add(s.to);
|
|
836
|
+
}
|
|
837
|
+
for (const s of touching.incoming) {
|
|
838
|
+
if (s.from !== engramId)
|
|
839
|
+
neighbors.add(s.from);
|
|
840
|
+
}
|
|
841
|
+
const count = deleteSynapsesTouching(this.config.rootPath, engramId);
|
|
842
|
+
if (neighbors.size > 0)
|
|
843
|
+
this.refreshObsidianLinks(...neighbors);
|
|
844
|
+
return count;
|
|
676
845
|
}
|
|
677
846
|
/**
|
|
678
847
|
* 添加 outgoing synapse。
|
|
@@ -683,7 +852,7 @@ export class EngramRepository {
|
|
|
683
852
|
* @returns 实际落盘的 synapse(其 id 是计算值)
|
|
684
853
|
*/
|
|
685
854
|
addOutgoingSynapse(fromId, synapse) {
|
|
686
|
-
|
|
855
|
+
const result = upsertSynapse(this.config.rootPath, {
|
|
687
856
|
from: fromId,
|
|
688
857
|
to: synapse.to,
|
|
689
858
|
kind: synapse.kind,
|
|
@@ -701,6 +870,8 @@ export class EngramRepository {
|
|
|
701
870
|
resolutionState: synapse.resolutionState,
|
|
702
871
|
language: this.language,
|
|
703
872
|
});
|
|
873
|
+
this.refreshObsidianLinks(fromId, synapse.to);
|
|
874
|
+
return result;
|
|
704
875
|
}
|
|
705
876
|
/**
|
|
706
877
|
* 删除 outgoing synapse。
|
|
@@ -714,6 +885,7 @@ export class EngramRepository {
|
|
|
714
885
|
return;
|
|
715
886
|
const path = join(this.config.rootPath, synapseRelativePath(target.id, target.kind));
|
|
716
887
|
deleteSynapseFile(path);
|
|
888
|
+
this.refreshObsidianLinks(target.from, target.to);
|
|
717
889
|
}
|
|
718
890
|
/**
|
|
719
891
|
* 更新 synapse 的 resolutionState(contradicts 专用)。
|
|
@@ -822,6 +994,12 @@ export class EngramRepository {
|
|
|
822
994
|
*/
|
|
823
995
|
updateVerificationStatus(id, status) {
|
|
824
996
|
this.mutateFrontmatter(id, (fm) => ({ ...fm, verificationStatus: status }));
|
|
997
|
+
// Task 3.4 Phase B:验证状态变化影响 prompt 的 lowConfidenceTopics
|
|
998
|
+
safeEmit({
|
|
999
|
+
type: "engram_verified",
|
|
1000
|
+
engramId: id,
|
|
1001
|
+
at: new Date().toISOString(),
|
|
1002
|
+
});
|
|
825
1003
|
}
|
|
826
1004
|
/**
|
|
827
1005
|
* 低层 frontmatter 修改:不触发 version++,不改 slug,不改 path。
|
|
@@ -876,6 +1054,11 @@ export class EngramRepository {
|
|
|
876
1054
|
path: orphanPath,
|
|
877
1055
|
message: `Markdown file without frontmatter: ${orphanPath} (either delete it or add frontmatter with a stable id)`,
|
|
878
1056
|
autoFixed: false,
|
|
1057
|
+
nextAction: {
|
|
1058
|
+
tool: "engram_create",
|
|
1059
|
+
argsHint: `{ title, content, kind, domainTags, createdBy } // 直接读取这个 markdown 的内容作为 engram body`,
|
|
1060
|
+
explanation: "Markdown 文件没有 frontmatter,所以不在 engram 索引里。如果是新记忆,用 engram_create 注册(把现有正文粘到 content);如果是废弃草稿,直接 rm 即可。",
|
|
1061
|
+
},
|
|
879
1062
|
});
|
|
880
1063
|
pendingManualReview.push(issues[issues.length - 1]);
|
|
881
1064
|
});
|
|
@@ -913,11 +1096,17 @@ export class EngramRepository {
|
|
|
913
1096
|
// 标记相关 synapse dangling
|
|
914
1097
|
const touching = listSynapsesForEngram(this.config.rootPath, oldId);
|
|
915
1098
|
if (touching.outgoing.length + touching.incoming.length > 0) {
|
|
1099
|
+
const danglingCount = touching.outgoing.length + touching.incoming.length;
|
|
916
1100
|
pendingManualReview.push({
|
|
917
1101
|
kind: "dangling_synapse",
|
|
918
1102
|
stableId: oldId,
|
|
919
|
-
message: `Engram ${oldId} was deleted but ${
|
|
1103
|
+
message: `Engram ${oldId} was deleted but ${danglingCount} synapse(s) still reference it (clean up manually or restore the engram)`,
|
|
920
1104
|
autoFixed: false,
|
|
1105
|
+
nextAction: {
|
|
1106
|
+
tool: "synapse_delete",
|
|
1107
|
+
argsHint: `{ id: "<synapseId>" } // synapseId 在每个 synapse 的 yaml id 字段,逐条删`,
|
|
1108
|
+
explanation: `被删 engram 还有 ${danglingCount} 条 synapse 指向它,这些 synapse 现在是悬空的。去 synapses/ 目录找出涉及该 engram 的 synapse,用 synapse_delete 逐条清理;或者用 engram_create 重建该 engram(让 synapse 重新有目标)。`,
|
|
1109
|
+
},
|
|
921
1110
|
});
|
|
922
1111
|
}
|
|
923
1112
|
}
|
|
@@ -988,6 +1177,35 @@ export class EngramRepository {
|
|
|
988
1177
|
});
|
|
989
1178
|
}
|
|
990
1179
|
}
|
|
1180
|
+
// 5. Obsidian 视图一致性(派生段 wikilinks)
|
|
1181
|
+
// 对每条 engram:checkObsidianView 检测派生段与权威源(synapse yaml)不一致,
|
|
1182
|
+
// 不一致 → regenerateObsidianLinks 重写派生段(wikilink target=文件名)。
|
|
1183
|
+
for (const [id, entry] of freshIndex.entries) {
|
|
1184
|
+
const absPath = join(this.config.rootPath, entry.path);
|
|
1185
|
+
if (!existsSync(absPath))
|
|
1186
|
+
continue;
|
|
1187
|
+
let file;
|
|
1188
|
+
try {
|
|
1189
|
+
file = readEngramFile(absPath);
|
|
1190
|
+
}
|
|
1191
|
+
catch {
|
|
1192
|
+
continue; // parse 错误由别处报告(或phan_markdown 路径)
|
|
1193
|
+
}
|
|
1194
|
+
const touching = listSynapsesForEngram(this.config.rootPath, id);
|
|
1195
|
+
const status = checkObsidianView(file, touching, freshIndex);
|
|
1196
|
+
if (!status.stale)
|
|
1197
|
+
continue;
|
|
1198
|
+
regenerateObsidianLinks(this.config.rootPath, id, this.language);
|
|
1199
|
+
fixes.push({
|
|
1200
|
+
kind: "obsidian_view_stale",
|
|
1201
|
+
stableId: id,
|
|
1202
|
+
path: entry.path,
|
|
1203
|
+
message: `Obsidian derived wikilinks regenerated (target=filename, display=title·kind)`,
|
|
1204
|
+
autoFixed: true,
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
// Task 3.4 Phase B:doctor 完成后 emit(doctor 可能 sweep/forget,engram 集合变化)
|
|
1208
|
+
safeEmit({ type: "doctor_completed", at: new Date().toISOString() });
|
|
991
1209
|
return {
|
|
992
1210
|
startedAt,
|
|
993
1211
|
finishedAt: now(),
|