@avantf/dsh-mem 0.9.0 → 0.10.0

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
@@ -1,38 +1,34 @@
1
1
  # @avantf/dsh-mem
2
2
 
3
- DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的**记忆**和一个**文档知识库**——
4
- 它跨会话记得你说过的事,也能把文档收进库里按语义查回来。装上后提供 7 个模型工具、三段用法提示
5
- 和两个主窗口标签页(记忆 / 知识)。
3
+ DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的**记忆**——它跨会话记得你说过的事,
4
+ 也能按语义、全文、实体、标题与时间把它找回来。装上后提供 3 个模型工具、一段用法提示
5
+ 和一个主窗口标签页(记忆)。
6
+
7
+ > 文档知识库不在本插件里:它已拆到独立插件 **`dsh-knowledges`**(`knowledge.db`、受管文档、`kb_*` 工具、
8
+ > 知识页与文档转换都在那边)。本插件既不读也不删 `~/.avantf/knowledge/**`。
6
9
 
7
10
  ## 主要功能
8
11
 
9
- - **7 个模型工具**
10
- - `mem_remember`:新增 / 改写 / 删除事实(写入后立刻做矛盾检测)
11
- - `mem_recall`:混合检索(语义 + 全文 + 实体)、三元组方向问答、实体探查、多实体联合推理、矛盾查询
12
- - `mem_admin`:列表 / **导出 / 导入 / 快照重建**。文件路径**只允许本次运行的数据根内**
12
+ - **3 个模型工具**
13
+ - `mem_remember`:新增 / 改写 / 删除事实(写入后立刻做矛盾检测),分两层——`journal`(做了什么 /
14
+ 聊了什么,必带 `event_date` 与短 `title`,同一事务复用同一 title)与 `note`(稳定事实 / 偏好 /
15
+ 约定 / 坑,可带 `relations` 三元组)
16
+ - `mem_recall`:`search` 内容检索(先对整句做字面精确匹配,再按查询形态返回事实集合 / 关系结构 /
17
+ 模糊检索;只给 `time` 就是纯时间召回)与 `titles` 标题检索(写 journal 前查重、标题纠错用)
18
+ - `mem_admin`:`list` 列举 / `contradictions` 列出未处理的矛盾对 / **导出 / 导入 / 快照重建**。
19
+ 文件路径**只允许本次运行的数据根内**
13
20
  (默认导出到 `<数据根>/backups/memory-YYYYMMDD-HHMMSS.json.br`,原子写,结果不含文档正文);
14
21
  `import` 按**文件头**自动识别 SQLite(只读合并另一份 `memory.db`,报告 `mode:'db'`)与 JSON 导出文档
15
22
  (`mode:'json'`);`restore_snapshot` 必须显式 `confirm: true`,且只接受**空 facts 表**,恢复后同一宿主
16
23
  进程立即可检索。
17
- - `kb_add` / `kb_list` / `kb_remove`:新增文档(文本 / 文件 / URL,或批量导入目录,同名拒绝)、
18
- 列表(带出受管 `.md` 的绝对路径与切片详情)、删除;
19
- **改一篇既有文档就是改那份 `.md`**(用文件工具改,索引自动跟随)
20
- - `kb_query`:跨「记忆事实 + 文档切片」的统一检索,结果带来源标注,可据此引用原文
21
- - 诊断、策展(pin / 归档恢复 / 反馈 / 矛盾裁决)与索引修复(重建索引、向量修复)是**操作者动作**:
22
- 在「记忆」「知识」页与 CLI 里可用,不占用模型工具面。
23
- - **记忆与知识一起检索**:对记忆事实与文档切片做统一排序,两类结果处在同一分数刻度上,
24
- 所以"我之前定过的约定"和"文档里怎么写的"能一次问出来。
25
- - **记忆是活的**:按活跃使用结算,支持有效期、遗忘、去重与修订链;同一主题的改动会被识别成
24
+ - 诊断、策展(pin / unpin / 归档恢复 / 矛盾裁决)与索引修复(重建索引、向量修复)是**操作者动作**:
25
+ 在「记忆」页与 CLI 里可用,不占用模型工具面。
26
+ - **混合检索**:语义 + 全文 + 实体三腿融合;`search` 按查询形态给出事实集合(多实体并列)、关系结构
27
+ (谁管理谁这类方向问句)、纯时间召回或模糊检索。
28
+ - **记忆是活的**:按活跃使用日结算、支持遗忘、去重与修订链;同一主题的改动会被识别成
26
29
  对旧事实的**修订**而不是简单叠加,写入时顺带做矛盾检测。
27
- - **资料随手入库**:文本、本地文件、目录、URL 都能收;Markdown 按标题分块,PDF、`.docx`、`.xlsx`
28
- 等常见格式也能直接入库(转换过程有问题会明确回报,不静默丢内容)。
29
- - **知识可维护**:每篇文档在数据目录里留一份可编辑的 Markdown 副本,用你自己的编辑器改,
30
- 插件会在其后自动把改动同步回索引;删除文档只删这份副本,你原来的文件不动。
31
- - **两个主窗口标签页**(排在 对话 / 轨迹 之后):**记忆**(浏览、编辑、归档恢复、反馈、矛盾裁决)
32
- 与**知识**(文档列表,每行可查看切片 / 编辑 / 打开目录 / 重新摄入 / 删除;另有跨库查询与入库——
33
- URL / 本地文件 / 本地目录 / 粘贴文本共用一个输入)。
34
- - 系统提示词里只加**三段**用法提示(何时该主动记忆、何时该先查记忆、何时该先查知识库 / 怎么改一篇
35
- 既有文档)。
30
+ - **一个主窗口标签页**(排在 对话 / 轨迹 之后):**记忆**(浏览、编辑、pin/unpin、归档恢复、矛盾裁决)。
31
+ - 系统提示词里只加**一段**用法提示(何时该主动记忆、journal 与 note 怎么分、同一事务怎么复用 title)。
36
32
 
37
33
  ## 接入 dsh
38
34
 
@@ -64,19 +60,21 @@ dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
64
60
  ```
65
61
 
66
62
  ```text
67
- 把 ~/docs/cgroup-v2.pdf 收到知识库的 ops 域下。
63
+ 上周做了什么?(按事件时间召回 journal 条目)
68
64
  ```
69
65
 
70
66
  ```text
71
- 看看知识库里关于「内存保护」的原文。
67
+ 谁负责发布流程?(关系问句直接返回关系结构)
72
68
  ```
73
69
 
74
70
  ```text
75
71
  把「每周四发布」改成「每周三发布」,并说明依据。
76
72
  ```
77
73
 
78
- 改一篇已经入库的文档:让模型"更新这篇文档"即可;也可以用任意文件工具直接编辑那份受管
79
- `.md`(先用 `kb_list` 拿到路径),改完会自动同步,「知识」页的「重新摄入」是手动兜底。
74
+ 查重与改标题:写 journal 前先让模型用 `mem_recall` 的 `titles` 找候选,读到候选就 `update`、
75
+ 读不到才 `add`;标题写错时用 `update` 带 `retitle: true` 纠正一次(旧标题保留为别名,仍能检索到)。
76
+
77
+ 要改一篇**文档**:那是 `dsh-knowledges` 的事(`kb_*` 工具与「知识」页在那边),本插件不提供文档入口。
80
78
 
81
79
  常见维护动作(在「记忆」页或 CLI 里):
82
80
 
@@ -87,8 +85,7 @@ dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
87
85
 
88
86
  ### 相关性门槛:严格 / 宽松
89
87
 
90
- 「记忆」「知识」页的查询面板有**严格 / 宽松**选择(工具面的 `mem_recall` / `kb_query` 也接受
91
- `floors: 'strict' | 'loose'`):
88
+ 「记忆」页的查询面板有**严格 / 宽松**选择(工具面的 `mem_recall` 也接受 `floors: 'strict' | 'loose'`):
92
89
 
93
90
  - **严格**(面板默认):只有明确够相关的才返回,所以"没有结果"是一个可信的答案。
94
91
  - **宽松**:门槛更低,用来找回边角相关的条目——仍有一条底线,不是无门槛。
@@ -97,17 +94,19 @@ dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
97
94
 
98
95
  ## 配置、数据与提示词
99
96
 
100
- - **数据目录**:默认 `~/.avantf`。记忆库、知识库(含受管文档副本)与模型缓存都在其中;
97
+ - **数据目录**:默认 `~/.avantf`。记忆库(`memory/memory.db`)、配置与提示词文件、备份与模型缓存都在其中;
101
98
  环境变量 `AVANTF_HOME` 可以改变它。
102
- - **配置**:`~/.avantf/configs/` 下的三份 YAML —— `common.yaml`(公共项,缺失时按注释模板自动创建)、
103
- `memory.yaml`、`knowledge.yaml`。只写你要改的项,其余取内置默认。
104
- - **提示词文件**:`~/.avantf/prompts/` 下的 `mem-memory-usage.md`、`mem-knowledge-usage.md`、
105
- `mem-kb-edit.md`。缺失或为空时插件写入默认内容,**只在启动时读一次**,改完重启生效。
106
- 可以按自己的领域改写:内置默认刻意不出现任何领域名词,只给判据与两种形状——
107
- - 事实型:`用户不吃香菜;订餐默认备注不要香菜。`(谁 / 什么范围 / 偏好或要求或术语是什么)
108
- - 规则型:`只有「注销式回购」减少总股本,库存股回购不减少——比较 EPS 影响时要区分。`
109
- - 不适合写进记忆:一次调查的过程叙述、数据表、命令与链接——依据归知识库文档,记忆里只留
110
- **结论 + 位置**(路径 / 来源 / 名称);敏感信息同理,只记存放位置,不记明文。
99
+ - **配置**:`~/.avantf/configs/` 下的两份 YAML —— `common.yaml`(公共项,缺失时按注释模板自动创建)与
100
+ `memory.yaml`。只写你要改的项,其余取内置默认。寿命按**活跃使用日**表达(`trust.base_lifetime_days` 是新事实的
101
+ 寿命,默认 **90**;`trust.max_retention_days` 是召回加强能换来的最长寿命,默认 **174**;
102
+ `lifecycle.purge_after_archived_days` 是归档后的回收窗口);没有单条到期字段,也没有日历兜底。
103
+ - **提示词文件**:`~/.avantf/prompts/` 下的 `mem-memory-usage.md`。缺失或为空时插件写入默认内容,
104
+ **只在启动时读一次**,改完重启生效。
105
+ 可以按自己的领域改写:内置默认刻意不出现任何领域名词,只给判据与记忆的两层——
106
+ - `journal`:一项实质性工作 / 话题完成或阶段性收敛时主动记一条,必须带 `event_date` 与短 `title`;
107
+ - `note`:稳定的偏好 / 约定 / 坑,直接记录,规则写成「当……应……」。
108
+ - 不适合写进记忆:临时聊天、一次性中间状态、容易查到的内容——依据归文档(`dsh-knowledges` 的文档库),
109
+ 记忆里只留 **结论 + 位置**(路径 / 来源 / 名称);敏感信息同理,只记存放位置,不记明文。
111
110
  - **升级前建议先备份 `~/.avantf`**:数据库迁移是单向的,用旧版本打开新库会被明确拒绝。
112
111
 
113
112
  ## 升级 / 卸载
@@ -120,8 +119,8 @@ dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
120
119
  dsh plugin --profile <PROFILE> remove @avantf/dsh-mem
121
120
  ```
122
121
 
123
- 两者都要重启 dsh 生效。卸载**不动 `~/.avantf`**:记忆库、知识库、模型缓存全部保留。DSH Desktop 在
124
- 「插件」页上卸载。
122
+ 两者都要重启 dsh 生效。卸载**不动 `~/.avantf`**:记忆库与模型缓存全部保留(`~/.avantf/knowledge/**` 属
123
+ `dsh-knowledges`,同样不动)。DSH Desktop 在「插件」页上卸载。
125
124
 
126
125
  ## 环境要求
127
126
 
package/lib/client.js CHANGED
@@ -4160,11 +4160,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4160
4160
  content: optionalText.optional(),
4161
4161
  fact_id: count.optional(),
4162
4162
  kind: optionalText.optional(),
4163
- ttl_days: count.optional(),
4164
4163
  reason: optionalText.optional(),
4165
4164
  source_ref: optionalText.optional(),
4166
4165
  event_date: optionalText.optional(),
4167
- valid_until: optionalText.optional(),
4168
4166
  title: optionalText.optional(),
4169
4167
  retitle: bool.optional(),
4170
4168
  relations: array(object({
@@ -4314,6 +4312,86 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4314
4312
  return violations && violations.length > 0 ? violations.join(";") : "";
4315
4313
  }
4316
4314
  //#endregion
4315
+ //#region ../contract/lib/time.js
4316
+ /**
4317
+ * The ONE place a stored timestamp turns into a string a human (or a model) reads.
4318
+ *
4319
+ * ── THE BUG THIS MODULE EXISTS FOR ───────────────────────────────────────────────────────────────
4320
+ *
4321
+ * `facts.created_at` / `updated_at` / `archived_at` (and `contradiction_log.detected_at`, and the
4322
+ * retrieval-health stamp) are written by SQLite's `DEFAULT CURRENT_TIMESTAMP`, which is **UTC**, and
4323
+ * `store/memory.ts` used to hand the raw text up to every surface. Two consequences, both reported as
4324
+ * "记忆时间显示不对":
4325
+ *
4326
+ * 1. the UI printed `2026-10-28 11:58:48` as if it were a local wall clock (Asia/Shanghai really
4327
+ * meant `19:58:48`), because a zone-less string cannot say otherwise;
4328
+ * 2. the MODEL received the same zone-less string next to `event_date`, which is a calendar DATE
4329
+ * with no time and no zone. Two different kinds of value, one shape — the model had to guess
4330
+ * which one it was looking at, and it cannot.
4331
+ *
4332
+ * ── THE SPLIT THIS MODULE ENFORCES ───────────────────────────────────────────────────────────────
4333
+ *
4334
+ * Storage stays UTC and stays byte-identical (`facts_io` export/import/snapshot is a compatibility
4335
+ * surface and is deliberately NOT routed through here). The fix is in the two DISPLAY faces:
4336
+ *
4337
+ * - {@link formatOffsetIso} — the MODEL face (`mem_recall` hits, `mem_admin list/detail/
4338
+ * contradictions`, `mem_admin stats`, the CLI and MCP that print those payloads): an ISO-8601
4339
+ * instant **with an explicit offset**, e.g. `2026-10-28T19:58:48+08:00`, rendered in the host's
4340
+ * local zone. The offset is part of the string, so the model needs no conversion and the value is
4341
+ * never ambiguous again — while `event_date` stays `2026-10-28`, a calendar date with no zone.
4342
+ * - {@link formatLocalHuman} — the HUMAN face (the 记忆 tab): `2026-10-28 19:58`, the same instant,
4343
+ * local, to the minute.
4344
+ *
4345
+ * Both read the input through the SAME parser ({@link parseUtcTs}) and the same instant, so the two
4346
+ * faces cannot disagree. The UI deliberately formats the model face's OWN string rather than
4347
+ * re-deriving one from the raw column: the input it gets already carries an offset, `parseUtcTs`
4348
+ * leaves an explicit offset alone, and there is therefore exactly ONE parse rule and ONE pair of
4349
+ * renderers in the repository — not a second opinion in the browser.
4350
+ *
4351
+ * Dependency-free on purpose: this module is reachable from the DSH client bundle through the
4352
+ * `@avantf/mem-contract/time` subpath (the bundle must not pull in the tool schemas / zod). It has no
4353
+ * DSH host and no DB, so the engine, the CLI and the MCP face can all use it.
4354
+ *
4355
+ * @module contract/time
4356
+ */
4357
+ /** Two-digit, zero-padded — the only formatting primitive the two renderers share. */
4358
+ function pad2(value) {
4359
+ return String(value).padStart(2, "0");
4360
+ }
4361
+ /**
4362
+ * `'YYYY-MM-DD HH:MM:SS'` (SQLite UTC) or ISO → epoch ms; null/garbage → null.
4363
+ *
4364
+ * A stamp with NO zone designator is read as UTC, not as local time. `Date.parse` treats a zone-less
4365
+ * ISO string as LOCAL, and everything in this store is written in UTC (`CURRENT_TIMESTAMP`, and
4366
+ * {@link formatUtcTs}), so handing one over as-is would shift every quota window and every decay
4367
+ * measurement by the host's UTC offset — a fact reinforced at 23:00 UTC+8 would look reinforced 8
4368
+ * hours earlier, and the 24h cap would open or close at the wrong moment. An explicit `Z` or
4369
+ * `±HH:MM` is left alone (that is what makes a `formatOffsetIso` output safe to feed back in).
4370
+ */
4371
+ function parseUtcTs(ts) {
4372
+ if (ts === null || ts === void 0 || ts === "") return null;
4373
+ const iso = ts.includes("T") ? ts : ts.replace(" ", "T");
4374
+ const zoned = /(?:Z|[+-]\d{2}:?\d{2})$/i.test(iso) ? iso : `${iso}Z`;
4375
+ const ms = Date.parse(zoned);
4376
+ return Number.isFinite(ms) ? ms : null;
4377
+ }
4378
+ /**
4379
+ * The HUMAN face: the same instant as local wall-clock text to the minute (`2026-10-28 19:58`).
4380
+ *
4381
+ * Seconds are deliberately dropped: the list/detail lines and the health line are read at a glance,
4382
+ * and `HH:MM` is the whole useful resolution there. `null` for an absent/unreadable value (same
4383
+ * contract as {@link formatOffsetIso}), so a caller prints its own 「未知」/`—` placeholder.
4384
+ *
4385
+ * NOT for `event_date` / `valid_from`: those are calendar dates the CALLER wrote (`2026-10-28`), with
4386
+ * no time and no zone. Running one through here would invent a midnight in the host's zone.
4387
+ */
4388
+ function formatLocalHuman(ts) {
4389
+ const ms = parseUtcTs(ts);
4390
+ if (ms === null) return null;
4391
+ const at = new Date(ms);
4392
+ return `${String(at.getFullYear())}-${pad2(at.getMonth() + 1)}-${pad2(at.getDate())} ${pad2(at.getHours())}:${pad2(at.getMinutes())}`;
4393
+ }
4394
+ //#endregion
4317
4395
  //#region src/client/summarize.ts
4318
4396
  /**
4319
4397
  * Short, page-friendly rendering of a tool result — never a JSON wall.
@@ -4775,7 +4853,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4775
4853
  * 2. Every remote call is normalized and surfaced: the raw response is logged
4776
4854
  * (prefixed `[avantf-mem]`) and rendered as an explicit status/error line, so
4777
4855
  * an empty result is never indistinguishable from a failed call.
4778
- * 3. Full DESIGN §12 surface: the tab does CRUD (edit/archive/restore/feedback) + fact detail
4856
+ * 3. Full DESIGN §12 surface: the tab does CRUD (edit/archive/restore) + fact detail
4779
4857
  * (entities/triples) + open contradictions. Generation stays external (Plan A) — no answer
4780
4858
  * synthesis.
4781
4859
  *
@@ -4792,6 +4870,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4792
4870
  * panels cannot drift apart.
4793
4871
  */
4794
4872
  const BTN = `${controls_module_css_default.button} ${controls_module_css_default.sm} ${controls_module_css_default.outline}`;
4873
+ /**
4874
+ * Every timestamp the 记忆 tab prints goes through here: local wall-clock text to the minute
4875
+ * (`2026-10-28 19:58`), or the caller's placeholder when the host sent nothing readable.
4876
+ *
4877
+ * This is a PLACEHOLDER policy, not a formatter — the formatting itself is
4878
+ * `formatLocalHuman` from `@avantf/mem-contract/time`, the same module the host renders the model
4879
+ * face with. The value arriving here is an instant WITH an offset (the host's `created_at` etc.), so
4880
+ * `Date.parse` inside that one renderer is unambiguous; nothing in this file parses a time itself.
4881
+ *
4882
+ * A calendar date (`event_date` / `valid_from`) must NOT come through here: it has no time and no
4883
+ * zone, and rendering it would invent a midnight in the viewer's zone.
4884
+ */
4885
+ const localTime = (ts, fallback = "—") => formatLocalHuman(ts) ?? fallback;
4795
4886
  const NOT_MOUNTED = "avantfMem Remote 未挂载:本插件自带的 typert contribution 挂载失败(详见控制台错误),暂时无法读取数据。";
4796
4887
  /** One 记忆 tab; the host `admin` list default is the same. */
4797
4888
  const MEMORY_PAGE_SIZE = 50;
@@ -4814,7 +4905,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4814
4905
  * How many `admin.detail` calls one search answer may trigger, to give its results the list's action
4815
4906
  * row (status / pin state / full text). A hard cap so a large exact page or a wide relation answer
4816
4907
  * cannot turn one keystroke into an unbounded fan-out; anything past the cap keeps the id-only
4817
- * actions (详情 / 编辑 when the excerpt is complete / 归档 / 反馈), which still work.
4908
+ * actions (详情 / 编辑 when the excerpt is complete / 归档), which still work.
4818
4909
  */
4819
4910
  const SEARCH_FACT_LOOKUPS_MAX = 20;
4820
4911
  /**
@@ -5053,7 +5144,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5053
5144
  */
5054
5145
  function FactDetailView(props) {
5055
5146
  const fact = props.fact;
5056
- return h("div", { className: controls_module_css_default.meta }, `标题:${fact.title ?? "(无标题)"}`, h("br"), `创建于 ${fact.created_at} / 更新于 ${fact.updated_at ?? "—"}`, h("br"), `实体:${(fact.entities ?? []).join("、") || "(无)"}`, h("br"), `三元组:${(fact.triples ?? []).map((t) => `${t.subj} —${t.pred}→ ${t.obj}(${t.confidence.toFixed(2)})`).join(";") || "(无)"}`, h("br"), `检索次数:${String(fact.retrieval_count ?? 0)}${fact.supersedes_id ? ` · 修订自 #${String(fact.supersedes_id)}` : ""} · 被断言 ${String(fact.assert_count ?? 1)} 次`, h("br"), `有效期:${fact.valid_from ?? "未知"} → ${fact.valid_to ?? (fact.status === "active" ? "仍在有效" : "未知")}` + (fact.superseded_by === null || fact.superseded_by === void 0 ? "" : ` · 被 #${String(fact.superseded_by)} 取代`), h("br"), `来源:${(fact.sources ?? []).length === 0 ? "(未记录)" : (fact.sources ?? []).map((s) => `${s.kind}:${s.ref}`).join(";")}`, h("br"), `关系:${(fact.relations ?? []).length === 0 ? "(无)" : (fact.relations ?? []).map((r) => `${r.subject} —${r.predicate}→ ${r.object}`).join(";")}`, fact.kind === "note" && fact.status === "active" ? h(RelationEditor, {
5147
+ return h("div", { className: controls_module_css_default.meta }, `标题:${fact.title ?? "(无标题)"}`, h("br"), `创建于 ${localTime(fact.created_at)} / 更新于 ${localTime(fact.updated_at)}`, h("br"), `实体:${(fact.entities ?? []).join("、") || "(无)"}`, h("br"), `三元组:${(fact.triples ?? []).map((t) => `${t.subj} —${t.pred}→ ${t.obj}(${t.confidence.toFixed(2)})`).join(";") || "(无)"}`, h("br"), `检索次数:${String(fact.retrieval_count ?? 0)}${fact.supersedes_id ? ` · 修订自 #${String(fact.supersedes_id)}` : ""} · 被断言 ${String(fact.assert_count ?? 1)} 次`, h("br"), `事件时间:${fact.valid_from ?? "未知"}` + (fact.superseded_by === null || fact.superseded_by === void 0 ? "" : ` · 被 #${String(fact.superseded_by)} 取代`), h("br"), `来源:${(fact.sources ?? []).length === 0 ? "(未记录)" : (fact.sources ?? []).map((s) => `${s.kind}:${s.ref}`).join(";")}`, h("br"), `关系:${(fact.relations ?? []).length === 0 ? "(无)" : (fact.relations ?? []).map((r) => `${r.subject} —${r.predicate}→ ${r.object}`).join(";")}`, fact.kind === "note" && fact.status === "active" ? h(RelationEditor, {
5057
5148
  fact,
5058
5149
  disabled: props.disabled,
5059
5150
  onSave: (relations) => {
@@ -5076,7 +5167,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5076
5167
  title: f.title ?? (f.kind === "journal" ? "(无标题)" : void 0),
5077
5168
  text: f.content,
5078
5169
  truncated: false,
5079
- meta: `#${String(f.fact_id)} · ${f.kind} · trust ${f.trust_score.toFixed(2)}` + (f.pinned === true ? " · 永久" : typeof f.remaining_days === "number" ? ` · 剩余 ≈${String(Math.ceil(f.remaining_days))} 活跃日` : "") + ` · 有用 ${String(f.helpful_count)} · ${f.created_at}` + (f.status === "archived" && f.archive_reason ? ` · 归档原因 ${f.archive_reason}` : "") + (f.status === "archived" && f.pinned === true ? " · 永久保留(不会被清理)" : ""),
5170
+ meta: `#${String(f.fact_id)} · ${f.kind} · trust ${f.trust_score.toFixed(2)}` + (f.pinned === true ? " · 永久" : typeof f.remaining_days === "number" ? ` · 剩余 ≈${String(Math.ceil(f.remaining_days))} 活跃日` : "") + ` · ${localTime(f.created_at)}` + (f.status === "archived" && f.archive_reason ? ` · 归档原因 ${f.archive_reason}` : "") + (f.status === "archived" && f.pinned === true ? " · 永久保留(不会被清理)" : ""),
5080
5171
  action: {
5081
5172
  fact_id: f.fact_id,
5082
5173
  status: f.status,
@@ -5109,7 +5200,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5109
5200
  title: entry.title,
5110
5201
  text: entry.content,
5111
5202
  truncated: entry.truncated === true,
5112
- meta: `#${String(entry.fact_id)} · ${entry.memory_kind} · ${entry.status} · ${entry.event_date ?? entry.created_at}` + (entry.source_ref === void 0 ? "" : ` · ${entry.source_ref}`),
5203
+ meta: `#${String(entry.fact_id)} · ${entry.memory_kind} · ${entry.status} · ${entry.event_date ?? localTime(entry.created_at)}` + (entry.source_ref === void 0 ? "" : ` · ${entry.source_ref}`),
5113
5204
  action,
5114
5205
  fromSearch: true
5115
5206
  };
@@ -5123,7 +5214,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5123
5214
  title: entry.title,
5124
5215
  text: entry.text,
5125
5216
  truncated: entry.truncated === true,
5126
- meta: `#${String(entry.fact_id)} · ${entry.memory_kind} · ${entry.created_at}`,
5217
+ meta: `#${String(entry.fact_id)} · ${entry.memory_kind} · ${localTime(entry.created_at)}`,
5127
5218
  action,
5128
5219
  fromSearch: true
5129
5220
  };
@@ -5331,7 +5422,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5331
5422
  * A CONTINUATION page calls it too (REVIEW-2026-10-27 P2-2): without that, every row a scroll brought
5332
5423
  * in kept the id-only action set until the query was run again. The lookup is per page, so the bound
5333
5424
  * above still holds — one page can resolve at most {@link SEARCH_FACT_LOOKUPS_MAX} of its rows and the
5334
- * rest keep the id-only actions (详情 / 编辑 / 归档 / 反馈), exactly like the first page's overflow.
5425
+ * rest keep the id-only actions (详情 / 编辑 / 归档), exactly like the first page's overflow.
5335
5426
  * The record is MERGED, so an append never drops what the first page resolved.
5336
5427
  *
5337
5428
  * The guard is the SEARCH's, not the page request's (`searchFactsGuard`): a page landing while the
@@ -5733,7 +5824,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5733
5824
  * The per-fact action buttons — ONE implementation for the admin list and every result shape.
5734
5825
  *
5735
5826
  * The regression this replaced: the actions were written inline in the list row, so when a search
5736
- * owned the view, a hit's 「详情」 had nowhere to render and there was no way to edit/archive/feedback
5827
+ * owned the view, a hit's 「详情」 had nowhere to render and there was no way to edit/archive
5737
5828
  * a fact found by search.
5738
5829
  *
5739
5830
  * `fromSearch` only decides what happens AFTER the write: a result set is what is on screen, so it
@@ -5794,28 +5885,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5794
5885
  setDetail(null);
5795
5886
  }
5796
5887
  }) : null,
5797
- fact.status === "active" ? h(Btn, {
5798
- key: "helpful",
5799
- label: "有用 +1",
5800
- disabled: busy,
5801
- onClick: () => {
5802
- run(`反馈有用 #${String(fact.fact_id)}`, (remote) => remote.remember({
5803
- action: "helpful",
5804
- fact_id: fact.fact_id
5805
- }));
5806
- }
5807
- }) : null,
5808
- fact.status === "active" ? h(Btn, {
5809
- key: "unhelpful",
5810
- label: "没用 −1",
5811
- disabled: busy,
5812
- onClick: () => {
5813
- run(`反馈没用 #${String(fact.fact_id)}`, (remote) => remote.remember({
5814
- action: "unhelpful",
5815
- fact_id: fact.fact_id
5816
- }));
5817
- }
5818
- }) : null,
5819
5888
  fact.status === "active" ? h(Btn, {
5820
5889
  key: "archive",
5821
5890
  label: "归档",
@@ -6158,7 +6227,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6158
6227
  }))), error !== void 0 ? h("div", { className: controls_module_css_default.error }, `错误:${error}`) : null, notice !== void 0 ? h("div", { className: controls_module_css_default.status }, notice) : null, health !== null ? h(HealthBlock, { stats: health }) : null, contradictions !== null ? contradictions.length === 0 ? h("div", { className: controls_module_css_default.status }, "没有未处理的矛盾。") : h("ul", { className: controls_module_css_default.list }, contradictions.map((c) => h("li", {
6159
6228
  key: c.contradiction_id,
6160
6229
  className: controls_module_css_default.item
6161
- }, `#${String(c.fact_a)}「${c.content_a}」 ↔ #${String(c.fact_b)}「${c.content_b}」`, h("span", { className: controls_module_css_default.meta }, `score ${c.score.toFixed(2)} · ${c.detected_at ?? ""}`), props.remote !== void 0 ? h("div", { className: pages_module_css_default.row }, h(Btn, {
6230
+ }, `#${String(c.fact_a)}「${c.content_a}」 ↔ #${String(c.fact_b)}「${c.content_b}」`, h("span", { className: controls_module_css_default.meta }, `score ${c.score.toFixed(2)} · ${localTime(c.detected_at, "")}`), props.remote !== void 0 ? h("div", { className: pages_module_css_default.row }, h(Btn, {
6162
6231
  label: `判 #${String(c.fact_a)} 错`,
6163
6232
  danger: true,
6164
6233
  disabled: busy,
@@ -6205,7 +6274,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6205
6274
  const pct = (v) => `${String(Math.round(v * 100))}%`;
6206
6275
  const kinds = Object.entries(r.by_kind).map(([kind, v]) => `${kind} ${String(v.queries)} 次(空 ${String(v.zero_results)})`).join(" · ");
6207
6276
  const truncated = r.embedding_truncated + r.output_truncated;
6208
- return h("div", { className: pages_module_css_default.detail }, pendingVectors === 0 ? null : h("div", { className: controls_module_css_default.status }, `⚠ 向量空间待迁移:${String(pendingVectors)} 条 ACTIVE 向量来自旧嵌入空间(宽度不符 ${String(vectors?.stale ?? 0)} · 其他表示 ${String(vectors?.space_stale ?? 0)})——语义腿对它们失效,正在后台分批重算;手动入口 \`avantf-mem vectors --fix\``), wal?.warning === null || wal?.warning === void 0 ? null : h("div", { className: controls_module_css_default.status }, `⚠ ${wal.warning}(当前 ${String(wal.bytes)} 字节)`), h("div", { className: controls_module_css_default.status }, `检索 ${String(r.queries)} 次 · 空结果 ${pct(r.zero_result_rate)} · 平均 ${r.avg_latency_ms.toFixed(1)}ms · 峰值 ${r.max_latency_ms.toFixed(1)}ms`), h("div", { className: controls_module_css_default.meta }, `语义腿在线 ${pct(r.semantic_live_rate)} · 平均命中 ${String(r.avg_results_per_query)} 条`), kinds === "" ? null : h("div", { className: controls_module_css_default.meta }, kinds), props.stats.sources === void 0 && props.stats.validity === void 0 ? null : h("div", { className: controls_module_css_default.meta }, `来源覆盖 ${props.stats.sources === void 0 ? "—" : pct(props.stats.sources.coverage)}(${String(props.stats.sources?.facts_with_source ?? 0)}/${String(props.stats.sources?.active ?? 0)}) · 事件时间覆盖 ${props.stats.validity === void 0 ? "—" : pct(props.stats.validity.coverage)}(${String(props.stats.validity?.facts_with_valid_from ?? 0)}/${String(props.stats.validity?.active ?? 0)})`), h("div", { className: controls_module_css_default.meta }, truncated === 0 ? "未被任何预算截断。" : `截断:嵌入 ${String(r.embedding_truncated)} · 输出 ${String(r.output_truncated)}${r.output_truncated > 0 ? "(调大 retrieval.max_output_tokens 或用 max_tokens 参数)" : ""}`), r.updated_at === null ? null : h("div", { className: controls_module_css_default.meta }, `更新于 ${r.updated_at}`));
6277
+ return h("div", { className: pages_module_css_default.detail }, pendingVectors === 0 ? null : h("div", { className: controls_module_css_default.status }, `⚠ 向量空间待迁移:${String(pendingVectors)} 条 ACTIVE 向量来自旧嵌入空间(宽度不符 ${String(vectors?.stale ?? 0)} · 其他表示 ${String(vectors?.space_stale ?? 0)})——语义腿对它们失效,正在后台分批重算;手动入口 \`avantf-mem vectors --fix\``), wal?.warning === null || wal?.warning === void 0 ? null : h("div", { className: controls_module_css_default.status }, `⚠ ${wal.warning}(当前 ${String(wal.bytes)} 字节)`), h("div", { className: controls_module_css_default.status }, `检索 ${String(r.queries)} 次 · 空结果 ${pct(r.zero_result_rate)} · 平均 ${r.avg_latency_ms.toFixed(1)}ms · 峰值 ${r.max_latency_ms.toFixed(1)}ms`), h("div", { className: controls_module_css_default.meta }, `语义腿在线 ${pct(r.semantic_live_rate)} · 平均命中 ${String(r.avg_results_per_query)} 条`), kinds === "" ? null : h("div", { className: controls_module_css_default.meta }, kinds), props.stats.sources === void 0 && props.stats.validity === void 0 ? null : h("div", { className: controls_module_css_default.meta }, `来源覆盖 ${props.stats.sources === void 0 ? "—" : pct(props.stats.sources.coverage)}(${String(props.stats.sources?.facts_with_source ?? 0)}/${String(props.stats.sources?.active ?? 0)}) · 事件时间覆盖 ${props.stats.validity === void 0 ? "—" : pct(props.stats.validity.coverage)}(${String(props.stats.validity?.facts_with_valid_from ?? 0)}/${String(props.stats.validity?.active ?? 0)})`), h("div", { className: controls_module_css_default.meta }, truncated === 0 ? "未被任何预算截断。" : `截断:嵌入 ${String(r.embedding_truncated)} · 输出 ${String(r.output_truncated)}${r.output_truncated > 0 ? "(调大 retrieval.max_output_tokens 或用 max_tokens 参数)" : ""}`), r.updated_at === null ? null : h("div", { className: controls_module_css_default.meta }, `更新于 ${localTime(r.updated_at)}`));
6209
6278
  }
6210
6279
  /** Client cordis-plugin name shown in boot diagnostics. */
6211
6280
  const name = "avantf-mem-client";
@@ -15,7 +15,7 @@ import type { AvantfLogger } from '../../mem-contract/index.js';
15
15
  * UNCOMMENTED line is warned about by the loader, so a wrong name here would be a trap rather than a
16
16
  * hint. `config_files.spec.ts` pins the ones this template names.
17
17
  */
18
- export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u5BB6\u65CF\u6839\uFF1A\u540C\u65F6\u51B3\u5B9A\u6570\u636E\u6839\u4E0E models/tools\uFF09\u51B3\u5B9A \u2014\u2014\n# \u53EA\u60F3\u6362\u6570\u636E\u6839\u3001\u4E0D\u642C\u6A21\u578B\u7F13\u5B58\u65F6\u7528 CLI \u7684 \u2464 \u663E\u5F0F\u53C2\u6570 avantf-mem --data-home <path>\uFF08\u63D2\u4EF6\u5219\u662F profile \u7684\n# dataHome\uFF0C\u5C5E \u2461\uFF09\u3002\u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u65F6\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-base-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 768\n# auto_download: true\n# auto_migrate: true # \u6362\u6A21\u578B/\u5BBD\u5EA6\u540E\u81EA\u52A8\u5206\u6279\u91CD\u7B97\u65E7\u7A7A\u95F4\u7684\u5411\u91CF\uFF08\u9ED8\u8BA4\u5F00\uFF09\uFF1B\u5173\u6389\u53EA\u5269\u542F\u52A8\u544A\u8B66 + vectors --fix\n# self_heal: # \u8BED\u4E49\u4F9D\u8D56\u81EA\u6108\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5141\u8BB8\u542F\u52A8\u671F\u5B89\u88C5\u7F3A\u5931\u7684 @huggingface/transformers\n# enabled: false # \u6253\u5F00\u540E\u8D70\u5E95\u5EA7 runtime-deps \u5171\u4EAB\u6839\uFF08base \u2265 0.7.0 / interface v6\uFF09\uFF1B\u5931\u8D25\u53EA\u964D\u7EA7\u5230 FTS+\u5B9E\u4F53\n# # \u88C5\u54EA\u4E2A\u5305 / \u4ECE\u54EA\u88C5 / \u63D0\u793A\u8BF4\u4EC0\u4E48\uFF1A\u89C1\u63D2\u4EF6\u81EA\u5E26\u7684 res/runtime-deps.yaml\uFF08\u968F\u5305\u53D1\u5E03\uFF09\n# native_self_heal: # \u539F\u751F\u53EF\u9009\u4F9D\u8D56\u81EA\u6108\uFF1Anodejieba / hnswlib-node\uFF08\u9ED8\u8BA4\u5173\uFF09\n# enabled: false # \u88C5\u54EA\u4E2A\u5305\u3001\u4ECE\u54EA\u88C5\u3001\u63D0\u793A\u8BF4\u4EC0\u4E48\uFF0C\u90FD\u7531\u63D2\u4EF6\u81EA\u5E26\u7684 res/runtime-deps.yaml \u51B3\u5B9A\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u817F\u95E8\u69DB\u662F\u7EDD\u5BF9\u95E8\u69DB\uFF0C\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF1B0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\n# # \u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-base-zh-v1.5/768\uFF08mean pooling\uFF09\u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F\uFF1A\u951A\u70B9 Jaccard \u7684**\u4E0B\u754C**\uFF1B\u5B9E\u9645\u8BC1\u636E\u95E8\u69DB\u518D\u6309\u951A\u70B9\u7A00\u6709\u5EA6\u52A0\u6743\uFF08\u00A720.19 \u7EED\uFF09\n# # min_score \u662F\u552F\u4E00\u7684**\u663E\u793A\u5206**\u95E8\u69DB\uFF0C\u4E5F\u662F UI \u663E\u793A\u7684\u90A3\u4E2A score\uFF1A\u5408\u5E76\u6C60\u6309\u6700\u5927\u878D\u5408\u5206\u5F52\u4E00\u5316\u540E\u7684\u503C\uFF0C\n# # \u662F**\u76F8\u5BF9\u91CF**\u2014\u2014\u540C\u4E00\u4E8B\u5B9E\u5728\u4E0D\u540C\u67E5\u8BE2\u91CC\u53EF\u80FD\u901A\u8FC7\u3001\u4E5F\u53EF\u80FD\u4E0D\u901A\u8FC7\uFF08\u53D6\u51B3\u4E8E\u540C\u4E00\u67E5\u8BE2\u91CC\u6709\u6CA1\u6709\u66F4\u5F3A\u7684\u5019\u9009\uFF09\u3002\n# # \u5B83\u53EA\u5E94\u7528\u4E00\u6B21\uFF1A\u5728\u5408\u5E76\u540E\u7684\u5019\u9009\u6C60\u4E0A\u3001\u6309 kind/source \u8FC7\u6EE4\u4E0E slice \u4E4B\u524D\uFF08mem_recall \u4E0E UI\n# # \u56E0\u6B64\u662F\u540C\u4E00\u4E2A\u523B\u5EA6\uFF09\uFF1B0 = \u5173\u95ED\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\uFF1B\u653E\u5BBD\u6863\u53EA\u964D\u817F\u95E8\u69DB\uFF0C\u4E0D\u964D\u5B83\u3002\n# # \u26A0\uFE0F \u88AB\u5B83\u4E22\u5F03\u7684\u6761\u76EE\u4E0D\u8FDB `dropped_by_floor`\uFF0C\u56E0\u6B64 UI \u7684\u300C\u4E25\u683C\u95E8\u69DB\u4E22\u5F03\u4E86 N \u6761\u300D**\u4E0D\u542B**\u8FD9\u4E00\u7C7B\n# # \uFF08\u4FE1\u5C01\u91CC\u5355\u72EC\u6709 dropped_by_score\uFF1B\u9762\u677F\u8FD9\u4E00\u7248\u8FD8\u6CA1\u6E32\u67D3\u5B83\uFF09\u3002\n# min_score: 0.5\n# # P-11 \u4E2D\u6587\u65F6\u95F4\u7A97\u817F\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5F00\u542F\u540E\uFF0C\u5E26\u4E2D\u6587\u65F6\u95F4\u8868\u8FBE\u7684\u67E5\u8BE2\uFF08\u4E0A\u4E2A\u6708 / \u6700\u8FD1\u4E09\u5929 / 2026\u5E749\u6708 \u2026\uFF09\n# # \u4F1A\u989D\u5916\u628A\"\u4E8B\u4EF6\u65F6\u95F4 valid_from \u843D\u5728\u8BE5\u7A97\u53E3\"\u7684\u6D3B\u8DC3\u4E8B\u5B9E\u4F5C\u4E3A\u4E00\u6761\u817F\u53C2\u4E0E\u878D\u5408\u3002\u5B83\u662F**\u52A0\u4E00\u6761\u817F\u3001\u4E0D\u662F\u8FC7\u6EE4\u5668**\uFF1A\n# # \u53EA\u589E\u52A0\u5019\u9009\uFF0C\u7EDD\u4E0D\u5220\u6389\u8BED\u4E49 / FTS / \u5B9E\u4F53\u817F\u5DF2\u7ECF\u627E\u5230\u7684\u4E8B\u5B9E\uFF1B\u6CA1\u6709\u4E8B\u4EF6\u65F6\u95F4\u7684\u4E8B\u5B9E\u4E0D\u4F5C\u4E3A\u8BE5\u817F\u5019\u9009\uFF0C\n# # \u4E5F**\u4E0D\u56DE\u9000\u5230\u5199\u5165\u65F6\u95F4 created_at**\u3002\u5B83\u5171\u7528 jaccard \u6743\u91CD\u4F4D\uFF08\u5BF9\u5916\u7684\u4E09\u952E\u6743\u91CD\u4E0D\u53D8\uFF09\u3002\n# # \u4EC0\u4E48\u65F6\u5019\u8BE5\u5F00\uFF1A\u5148\u7528 mem_admin stats \u770B validity.coverage\uFF08\u6709\u591A\u5C11\u6D3B\u8DC3\u4E8B\u5B9E\u5E26\u4E8B\u4EF6\u65F6\u95F4\uFF09\uFF0C\n# # \u518D\u7528\u56DE\u5F52\u7F51\u786E\u8BA4\u5B83\u786E\u5B9E\u6539\u5584\u65F6\u95F4\u7C7B\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u7387\u22480 \u65F6\u5B83\u7B49\u4E8E\u6CA1\u5F00\u3002\u5199\u5165\u4E8B\u4EF6\u65F6\u95F4\u7528 mem_remember \u7684 event_date\u3002\n# time_window: false\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust:\n# decay_per_day: 0.0055\u2026\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
18
+ export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u5BB6\u65CF\u6839\uFF1A\u540C\u65F6\u51B3\u5B9A\u6570\u636E\u6839\u4E0E models/tools\uFF09\u51B3\u5B9A \u2014\u2014\n# \u53EA\u60F3\u6362\u6570\u636E\u6839\u3001\u4E0D\u642C\u6A21\u578B\u7F13\u5B58\u65F6\u7528 CLI \u7684 \u2464 \u663E\u5F0F\u53C2\u6570 avantf-mem --data-home <path>\uFF08\u63D2\u4EF6\u5219\u662F profile \u7684\n# dataHome\uFF0C\u5C5E \u2461\uFF09\u3002\u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u65F6\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-base-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 768\n# auto_download: true\n# auto_migrate: true # \u6362\u6A21\u578B/\u5BBD\u5EA6\u540E\u81EA\u52A8\u5206\u6279\u91CD\u7B97\u65E7\u7A7A\u95F4\u7684\u5411\u91CF\uFF08\u9ED8\u8BA4\u5F00\uFF09\uFF1B\u5173\u6389\u53EA\u5269\u542F\u52A8\u544A\u8B66 + vectors --fix\n# self_heal: # \u8BED\u4E49\u4F9D\u8D56\u81EA\u6108\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5141\u8BB8\u542F\u52A8\u671F\u5B89\u88C5\u7F3A\u5931\u7684 @huggingface/transformers\n# enabled: false # \u6253\u5F00\u540E\u8D70\u5E95\u5EA7 runtime-deps \u5171\u4EAB\u6839\uFF08base \u2265 0.7.0 / interface v6\uFF09\uFF1B\u5931\u8D25\u53EA\u964D\u7EA7\u5230 FTS+\u5B9E\u4F53\n# # \u88C5\u54EA\u4E2A\u5305 / \u4ECE\u54EA\u88C5 / \u63D0\u793A\u8BF4\u4EC0\u4E48\uFF1A\u89C1\u63D2\u4EF6\u81EA\u5E26\u7684 res/runtime-deps.yaml\uFF08\u968F\u5305\u53D1\u5E03\uFF09\n# native_self_heal: # \u539F\u751F\u53EF\u9009\u4F9D\u8D56\u81EA\u6108\uFF1Anodejieba / hnswlib-node\uFF08\u9ED8\u8BA4\u5173\uFF09\n# enabled: false # \u88C5\u54EA\u4E2A\u5305\u3001\u4ECE\u54EA\u88C5\u3001\u63D0\u793A\u8BF4\u4EC0\u4E48\uFF0C\u90FD\u7531\u63D2\u4EF6\u81EA\u5E26\u7684 res/runtime-deps.yaml \u51B3\u5B9A\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u817F\u95E8\u69DB\u662F\u7EDD\u5BF9\u95E8\u69DB\uFF0C\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF1B0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\n# # \u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-base-zh-v1.5/768\uFF08mean pooling\uFF09\u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F\uFF1A\u951A\u70B9 Jaccard \u7684**\u4E0B\u754C**\uFF1B\u5B9E\u9645\u8BC1\u636E\u95E8\u69DB\u518D\u6309\u951A\u70B9\u7A00\u6709\u5EA6\u52A0\u6743\uFF08\u00A720.19 \u7EED\uFF09\n# # min_score \u662F\u552F\u4E00\u7684**\u663E\u793A\u5206**\u95E8\u69DB\uFF0C\u4E5F\u662F UI \u663E\u793A\u7684\u90A3\u4E2A score\uFF1A\u5408\u5E76\u6C60\u6309\u6700\u5927\u878D\u5408\u5206\u5F52\u4E00\u5316\u540E\u7684\u503C\uFF0C\n# # \u662F**\u76F8\u5BF9\u91CF**\u2014\u2014\u540C\u4E00\u4E8B\u5B9E\u5728\u4E0D\u540C\u67E5\u8BE2\u91CC\u53EF\u80FD\u901A\u8FC7\u3001\u4E5F\u53EF\u80FD\u4E0D\u901A\u8FC7\uFF08\u53D6\u51B3\u4E8E\u540C\u4E00\u67E5\u8BE2\u91CC\u6709\u6CA1\u6709\u66F4\u5F3A\u7684\u5019\u9009\uFF09\u3002\n# # \u5B83\u53EA\u5E94\u7528\u4E00\u6B21\uFF1A\u5728\u5408\u5E76\u540E\u7684\u5019\u9009\u6C60\u4E0A\u3001\u6309 kind/source \u8FC7\u6EE4\u4E0E slice \u4E4B\u524D\uFF08mem_recall \u4E0E UI\n# # \u56E0\u6B64\u662F\u540C\u4E00\u4E2A\u523B\u5EA6\uFF09\uFF1B0 = \u5173\u95ED\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\uFF1B\u653E\u5BBD\u6863\u53EA\u964D\u817F\u95E8\u69DB\uFF0C\u4E0D\u964D\u5B83\u3002\n# # \u26A0\uFE0F \u88AB\u5B83\u4E22\u5F03\u7684\u6761\u76EE\u4E0D\u8FDB `dropped_by_floor`\uFF0C\u56E0\u6B64 UI \u7684\u300C\u4E25\u683C\u95E8\u69DB\u4E22\u5F03\u4E86 N \u6761\u300D**\u4E0D\u542B**\u8FD9\u4E00\u7C7B\n# # \uFF08\u4FE1\u5C01\u91CC\u5355\u72EC\u6709 dropped_by_score\uFF1B\u9762\u677F\u8FD9\u4E00\u7248\u8FD8\u6CA1\u6E32\u67D3\u5B83\uFF09\u3002\n# min_score: 0.5\n# # P-11 \u4E2D\u6587\u65F6\u95F4\u7A97\u817F\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5F00\u542F\u540E\uFF0C\u5E26\u4E2D\u6587\u65F6\u95F4\u8868\u8FBE\u7684\u67E5\u8BE2\uFF08\u4E0A\u4E2A\u6708 / \u6700\u8FD1\u4E09\u5929 / 2026\u5E749\u6708 \u2026\uFF09\n# # \u4F1A\u989D\u5916\u628A\"\u4E8B\u4EF6\u65F6\u95F4 valid_from \u843D\u5728\u8BE5\u7A97\u53E3\"\u7684\u6D3B\u8DC3\u4E8B\u5B9E\u4F5C\u4E3A\u4E00\u6761\u817F\u53C2\u4E0E\u878D\u5408\u3002\u5B83\u662F**\u52A0\u4E00\u6761\u817F\u3001\u4E0D\u662F\u8FC7\u6EE4\u5668**\uFF1A\n# # \u53EA\u589E\u52A0\u5019\u9009\uFF0C\u7EDD\u4E0D\u5220\u6389\u8BED\u4E49 / FTS / \u5B9E\u4F53\u817F\u5DF2\u7ECF\u627E\u5230\u7684\u4E8B\u5B9E\uFF1B\u6CA1\u6709\u4E8B\u4EF6\u65F6\u95F4\u7684\u4E8B\u5B9E\u4E0D\u4F5C\u4E3A\u8BE5\u817F\u5019\u9009\uFF0C\n# # \u4E5F**\u4E0D\u56DE\u9000\u5230\u5199\u5165\u65F6\u95F4 created_at**\u3002\u5B83\u5171\u7528 jaccard \u6743\u91CD\u4F4D\uFF08\u5BF9\u5916\u7684\u4E09\u952E\u6743\u91CD\u4E0D\u53D8\uFF09\u3002\n# # \u4EC0\u4E48\u65F6\u5019\u8BE5\u5F00\uFF1A\u5148\u7528 mem_admin stats \u770B validity.coverage\uFF08\u6709\u591A\u5C11\u6D3B\u8DC3\u4E8B\u5B9E\u5E26\u4E8B\u4EF6\u65F6\u95F4\uFF09\uFF0C\n# # \u518D\u7528\u56DE\u5F52\u7F51\u786E\u8BA4\u5B83\u786E\u5B9E\u6539\u5584\u65F6\u95F4\u7C7B\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u7387\u22480 \u65F6\u5B83\u7B49\u4E8E\u6CA1\u5F00\u3002\u5199\u5165\u4E8B\u4EF6\u65F6\u95F4\u7528 mem_remember \u7684 event_date\u3002\n# time_window: false\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust: # \u4FE1\u4EFB\u4E0E\u9057\u5FD8\uFF1A\u65CB\u94AE\u662F\u300C\u5929\u6570 + \u5F62\u72B6\u300D\uFF0C\u4E0D\u662F\u659C\u7387\n# base_lifetime_days: 90 # \u951A\u70B9\uFF1A0.5 \u8D77\u70B9 \u2192 0 \u7684\u6D3B\u8DC3\u65E5\u6570\uFF08\u65B0\u4E8B\u5B9E\u4E0D\u88AB\u53EC\u56DE\u65F6\u7684\u5BFF\u547D\uFF09\n# max_retention_days: 174 # \u53EC\u56DE\u52A0\u5F3A\u6700\u591A\u80FD\u6362\u6765\u7684\u6D3B\u8DC3\u65E5\u6570\uFF1Bceiling \u7531\u5B83\u53CD\u89E3\uFF08accelerating \u4E0B \u2248 0.7492\uFF09\n# decay_shape: accelerating # accelerating\uFF08\u9ED8\u8BA4\uFF09| linear\uFF08\u56DE\u6EDA\u6863\uFF1A\u914D 153 \u65F6 ceiling \u56DE\u5230 0.85\uFF09\n# decay_curvature: 0.05 # accelerating \u7684\u4E0B\u9650\u9879 a\uFF0C\u5408\u6CD5\u533A\u95F4 (0, 0.5]\uFF1Blinear \u4E0B\u4E0D\u53C2\u4E0E\n# recall_gain_below_base: 0.1 # \u4F4E\u4E8E\u57FA\u51C6 0.5 \u65F6\u6BCF\u6B21\u53EC\u56DE\u52A0\u591A\u5C11\uFF08\u5C01\u9876\u57FA\u51C6\uFF1A0.05 \u8D77 5 \u6B21\u5230 0.5\uFF09\n# recall_gain_above_base: 0.01 # \u9AD8\u4E8E\u57FA\u51C6 0.5 \u65F6\u6BCF\u6B21\u53EC\u56DE\u52A0\u591A\u5C11\uFF08\u5C01\u9876\u4E0A\u9762\u53CD\u89E3\u51FA\u7684 ceiling\uFF09\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
19
19
  /**
20
20
  * Ensure the common config exists with content, and hand back the path either way.
21
21
  *