@avantf/dsh-mem 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # @avantf/dsh-mem
2
2
 
3
- DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的**记忆**和一个**文档知识库**,
4
- 以进程内 Cordis 插件的形式提供 8 个模型工具、三段用法提示和两个主窗口标签页。
3
+ DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的**记忆**和一个**文档知识库**——
4
+ 它跨会话记得你说过的事,也能把文档收进库里按语义查回来。装上后提供 8 个模型工具、三段用法提示
5
+ 和两个主窗口标签页(记忆 / 知识)。
5
6
 
6
7
  ## 主要功能
7
8
 
@@ -13,216 +14,110 @@ DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的
13
14
  - `kb_add` / `kb_list` / `kb_remove` / `kb_reindex`:新增文档(文本 / 文件 / URL,或批量导入目录,
14
15
  同名拒绝)、列表(带出受管 `.md` 的绝对路径与切片详情)、删除、重建索引;
15
16
  **改一篇既有文档就是改那份 `.md`**(用文件工具改,索引自动跟随)
16
- - `kb_query`:跨「记忆事实 + 文档切片」的统一检索,结果带来源标注
17
- - **混合检索 + 联合融合**:库内三路(语义 / FTS bm25 / 实体 Jaccard,另有 HRR 实体探查腿),
18
- 跨库在合并候选池上做联合归一化——记忆与文档切片因此处于同一分数刻度。
19
- - **记忆是活的**:活跃使用日时钟,结算 / TTL / 遗忘 / 闲置 / 清理五段生命周期,去重与修订链,
20
- 写入即做的矛盾检测;派生状态(实体 / 三元组 / HRR bundle)跟着规则版本走,可批量重抽。
21
- - **文档知识库**:Markdown 标题感知分块、摄入期抽实体(检索期零重扫)、可增量重建索引;
22
- `domain` 受 `knowledge.domains` 清单约束(默认 `design/api/ops/research/notes`,显式 `[]` = 不限制),
23
- `source` 可留空(缺省 `default`)。可直接摄入文本与 **PDF**(抽取文本层,中文 CID 字体可用;
24
- 扫描件会明确报错),并把 **pandoc 能读的格式**(`.docx`/`.odt`/`.epub`/`.html`/`.tex`/`.rst`/`.csv`
25
- 等)与 **`.xlsx`** 先转成 Markdown 再入库(用了哪条转换器随 `converter` 回报且带版本,如
26
- `pandoc-3.11`;没能带过来的内容随 `warnings` 透出)。其他二进制(图片 / pptx / 旧版
27
- `.doc`/`.xls`/`.ppt` / 可执行文件)会被拒绝并说明检测到的类型;GBK/GB18030 等中文旧编码会自动解码
28
- 并在结果里标出 `encoding`。
29
- - **知识可维护**:每篇文档在 `~/.avantf/knowledge/docs/<domain>/<source>/<title>.md` 留一份可编辑副本
30
- (frontmatter 记着它属于哪篇文档)——用你自己的编辑器改,插件会在**其后任一工具调用结束时**自动把改动同步
31
- 回索引(先一次 `stat` 判漂移、再用正文哈希裁决,只重摄入真正改过的),「知识」页的「重新摄入」是手动兜底;
32
- 删除文档会连同这份副本一起删除,`source_uri` 指向的原文件始终不被改动。
33
- - **可插拔、可降级**:语义后端 / 向量库都走内部注册表;模型、hnswlib、分词器任一缺失都自动降级
34
- (FTS + 实体 / numpy 向量库 / 正则抽取),不会因此退出。
35
- - **0.5.0 起不再有结果重排**:`Reranker` 接口、`rerank.*` 配置键、公开面的 `registerReranker` 与结果/统计里的
36
- `rerank_*` 字段已整体移除(旧配置里的 `rerank:` 段会被当作未知键告警并忽略);可注册的检索面只剩语义后端与向量库。
37
- - **两个主窗口标签页**(排在 对话 / 轨迹 之后):**记忆**(浏览、编辑、归档恢复、反馈、矛盾裁决)与
38
- **知识**(文档列表,每行可查看切片 / 编辑 / 打开目录 / 重新摄入 / 删除;另有跨库查询与入库——
17
+ - `kb_query`:跨「记忆事实 + 文档切片」的统一检索,结果带来源标注,可据此引用原文
18
+ - **记忆与知识一起检索**:对记忆事实与文档切片做统一排序,两类结果处在同一分数刻度上,
19
+ 所以"我之前定过的约定"和"文档里怎么写的"能一次问出来。
20
+ - **记忆是活的**:按活跃使用结算,支持有效期、遗忘、去重与修订链;同一主题的改动会被识别成
21
+ 对旧事实的**修订**而不是简单叠加,写入时顺带做矛盾检测。
22
+ - **资料随手入库**:文本、本地文件、目录、URL 都能收;Markdown 按标题分块,PDF、`.docx`、`.xlsx`
23
+ 等常见格式也能直接入库(转换过程有问题会明确回报,不静默丢内容)。
24
+ - **知识可维护**:每篇文档在数据目录里留一份可编辑的 Markdown 副本,用你自己的编辑器改,
25
+ 插件会在其后自动把改动同步回索引;删除文档只删这份副本,你原来的文件不动。
26
+ - **两个主窗口标签页**(排在 对话 / 轨迹 之后):**记忆**(浏览、编辑、归档恢复、反馈、矛盾裁决)
27
+ 与**知识**(文档列表,每行可查看切片 / 编辑 / 打开目录 / 重新摄入 / 删除;另有跨库查询与入库——
39
28
  URL / 本地文件 / 本地目录 / 粘贴文本共用一个输入)。
40
29
  - 系统提示词里只加**三段**用法提示(何时该主动记忆、何时该先查记忆、何时该先查知识库 / 怎么改一篇
41
- 既有文档),不解释内部保留策略。
30
+ 既有文档)。
42
31
 
43
32
  ## 接入 dsh
44
33
 
45
34
  ```bash
46
- # 1) 装这两个包:插件本身,以及家族底座。底座是插件的 peer;不开 autoInstallPeers 的包管理器
47
- # (pnpm / yarn)不会跟着装上,所以要显式装。
48
- # 版本号建议写死:pnpm ≥ 10 的 minimumReleaseAge 会避开刚发布的版本,解析到的旧版本可能不认识
49
- # 当前宿主(它的 peer 区间不含这个 dsh),那一单会被宿主的安装门禁直接拒绝并把 profile 回滚。
50
- dsh plugin --profile <PROFILE> add @avantf/dsh-plugin-base@<version>
35
+ # 把插件装进你要用的 profile(依赖会自动装好,不用管别的)
51
36
  dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
52
37
 
53
- # 2) 原生依赖只剩**可选**的加速件(分词 / 向量索引 / 嵌入模型)。它们缺失只会降级检索质量,
54
- # 不影响记忆库本身——存储层用的是运行时自带的 node:sqlite,没有需要编译的数据库模块。
55
- cd ~/.dsh/profiles/<PROFILE>
56
- pnpm approve-builds --all # 勾选 nodejieba / hnswlib-node / onnxruntime,可按需
38
+ # 然后重启 dsh 生效
57
39
  ```
58
40
 
59
- ```yaml
60
- # 3) 挂载插件:装完通常什么都不用做。
61
- #
62
- # 本包声明了组合包(dsh.bundle.patch → 随包的 cordis.patch.yml),所以 `dsh plugin add` 装它时会
63
- # 顺手把这个包选进 profile 的 dsh.profile.bundles(实测),挂载行由随包的 patch 插入;DSH Desktop
64
- # 的「插件」页走的也是这条。只有两种情况才需要自己写那一行:
65
- # · 用 npm / yarn 装(它们不认识 dsh.profile.bundles);
66
- # · 手工编辑 profile 的包清单,而没有把本包选进 bundles。
67
- # 两条路都做也不会挂两次:当前 dsh(0.1.7 / 0.2.0 线)的 loader 按条目 id 去重,只挂载一次(实测);
68
- # 但 0.1.5 线(仍在本包 peer 区间内)上重复的行 id 会让 profile 树加载失败——所以始终二选一。
69
- # · 前提:自动选中只在 `dsh plugin add` **跑完**那一刻发生。若它中途被构建提示(pnpm 的
70
- # approve-builds)拦下(ERR_PNPM_IGNORED_BUILDS、非零退出),依赖装上了但组合包**不会被选中**,
71
- # 事后再 add / install 也不会补选——先答完提示再装,或到 DSH Desktop 的「插件」页启用这个组合包。
72
- #
73
- # 手写的那一行,放在 ~/.dsh/profiles/<PROFILE>/cordis.patch.yml(不存在就新建):
74
- - insert:
75
- - id: avantf-mem
76
- name: '@avantf/dsh-mem'
77
- config:
78
- mode: cordis # 进程内插件
79
- # dataHome 建议省略(默认 ~/.avantf)。它是配置值,会被 AVANTF_HOME 压过。
80
- ```
81
-
82
- ```bash
83
- # 4) 重启 dsh(宿主半边需重启,浏览器半边会热重载)
84
- dsh web
85
- ```
86
-
87
- ### DSH Desktop
41
+ 版本号可以不写,但建议指定一个明确版本。
88
42
 
89
- Desktop 用的是同一套 profile(profile 名 `desktop`),差别只有三点:
90
-
91
- - **界面上只能装组合包**。Desktop 的「插件」页与 `plugin_manager` 只接受声明了 `dsh.bundle.patch`
92
- 的包(本包已声明):在页面上装它,它会把这个包选进 `dsh.profile.bundles`,并应用随包的
93
- `cordis.patch.yml`——不用手写挂载行。
94
- - **用它自带的 dsh**:`<安装目录>/resources/runtime/cli/bin/dsh.cmd`(或在设置里把 `dsh` 装进 PATH)。
95
- - **重启应用**才生效:Desktop 的宿主半边与浏览器半边一起启动,不像 Web 那样热更。
96
-
97
- Desktop 的宿主进程是 **Electron**(`NODE_MODULE_VERSION` 与同版本 Node 不同)。这正是本插件不依赖任何
98
- SQLite 绑定的原因:存储层用运行时自带的 `node:sqlite`,它就在跑插件的那个进程里,没有预编译产物要匹配、
99
- 也不需要用户装 C++ 工具链。启动日志里 `sqlite=node:sqlite <版本>` 就是它;两个宿主写的是同一个 SQLite
100
- 文件格式,换宿主不需要迁移数据。
101
-
102
- - 插件的 `@deepseek-ai/*` peer 由宿主提供(profile 上层的运行时解析表),`dsh plugin add` 不会另装一份。
103
- **不要在 profile 里再装** `cordis` / `schemastery` / `dsh-tools`——两份对象身份会让工具与 typert
104
- 注册表对不上。
105
- - `zod` 同样是**宿主提供的 required peer**(区间 `>=4.4.3 <5`,宿主的 dsh 自带):工具的 wire 与参数
106
- schema 都用**这一份**。**不要在 profile 里单独装一份 `zod`**——两个副本的 schema 类型身份不同,
107
- 跨副本传 schema 对象会在运行期对不上。
43
+ **DSH Desktop**:在「插件」页里安装同一个包即可,装完重启应用;不用动命令行。
108
44
 
109
45
  ## 怎么用
110
46
 
111
- 工具是给模型用的,通常不用你手写;这里是最常见的几种用法。
47
+ 工具是给模型用的,通常不用你手写;用日常说法提要求就行。最常见的几种:
112
48
 
113
49
  ```text
114
50
  记住这条:发布窗口是每周四 20:00;验收标准是灰度零回滚。
115
51
  ```
116
52
 
117
53
  ```text
118
- 我之前定过的数据目录约定是什么?(→ mem_recall,返回命中事实与来源)
54
+ 我之前定过的数据目录约定是什么?(返回命中事实与来源)
119
55
  ```
120
56
 
121
57
  ```text
122
- 把 ~/docs/cgroup-v2.pdf 收到知识库的 ops 域下。(→ kb_add source_uri,转换器与告警随结果回报)
58
+ 把 ~/docs/cgroup-v2.pdf 收到知识库的 ops 域下。
123
59
  ```
124
60
 
125
61
  ```text
126
- 看看知识库里关于「内存保护」的原文。(→ kb_query,结果带 source_ref,可据此引用)
62
+ 看看知识库里关于「内存保护」的原文。
127
63
  ```
128
64
 
129
- 改一篇已经入库的文档:先用 `kb_list` 拿到那份 `.md` 的路径,再用**任意文件工具**改它——改完不必手动回填,
130
- 插件会在其后任一工具调用结束时自动同步(「重新摄入」是手动兜底);或直接对模型说"更新这篇文档"。
65
+ ```text
66
+ 把「每周四发布」改成「每周三发布」,并说明依据。
67
+ ```
68
+
69
+ 改一篇已经入库的文档:让模型"更新这篇文档"即可;也可以用任意文件工具直接编辑那份受管
70
+ `.md`(先用 `kb_list` 拿到路径),改完会自动同步,「知识」页的「重新摄入」是手动兜底。
131
71
 
132
72
  常见维护动作:
133
73
 
134
74
  ```text
135
- 检查记忆库的检索与索引状态。(→ mem_admin trust_diagnose / vectors_diagnose / stats)
136
- 把矛盾检测的积压扫一遍。(→ mem_admin contradict_check,再对每条判 true_positive / false_positive)
75
+ 检查记忆库的检索与索引状态。
76
+ 把矛盾检测的积压扫一遍,逐条裁决。
137
77
  ```
138
78
 
139
- ### 相关性门槛(严格 / 宽松)
79
+ ### 相关性门槛:严格 / 宽松
140
80
 
141
- 检索对每条腿都有一条**绝对**相关性门槛(打在每条腿的原始分上、融合之前):语义余弦
142
- `min_semantic_similarity`(默认 `0.5`)、FTS 腿逐行命中的**不同查询词元数** `min_fts_terms`(默认 `2`)、
143
- 实体 Jaccard `min_jaccard`(默认 `0.2`)——分数等于门槛保留,`0` = 关闭该腿(改在
144
- `~/.avantf/configs/common.yaml` 的 `retriever` 节)。
81
+ 「记忆」「知识」页的查询面板有**严格 / 宽松**选择(工具面的 `mem_recall` / `kb_query` 也接受
82
+ `floors: 'strict' | 'loose'`):
145
83
 
146
- - **两个档位(UI 选择器 / `floors` 参数)**:「知识」「记忆」页的查询面板有「严格 / 宽松」选择,`mem_recall.search`
147
- 与 `kb_query` 也接受 `floors: 'strict' | 'loose'`。`strict` = 用配置值、**不**自动放宽(面板默认它,好让空结果
148
- 有明确说法);`loose` = 直接宽松档,换成一组仍有绝对底线的值(`0.40 / ⌈词元数/2⌉ / 0.15`)——所以宽松档
149
- 一样答得出"确实没有相关结果",不是"无门槛"。
150
- - **不传 `floors` 时的默认策略:严格档为空就自动放宽一次**:先按严格档跑;**仅当**它一条都没命中、且确实有
151
- 条目被门槛丢掉时,才用宽松档补跑一遍。只有宽松档真的产出结果才替换答案,结果里置 `relaxed: true`,面板显示
152
- 「(已自动放宽门槛)」;放宽也救不回时**原样返回严格档结果**(含"被门槛丢掉 N 条"的报告)。跨库查询只对
153
- **合并后**的结果做这个决定,免得一条空库把放宽后的尾部混进另一条库的严格命中。
154
- - **可解释性**:结果带生效门槛 `floors` 与每腿丢弃数 `dropped_by_floor`,所以「门槛挡掉的空」与「本来就没有」
155
- 能分开说——面板因此分别给出「严格门槛丢弃了 N 条,可切「宽松」重试」与「(宽松档下)确实没有相关结果」。
84
+ - **严格**(面板默认):只有明确够相关的才返回,所以"没有结果"是一个可信的答案。
85
+ - **宽松**:门槛更低,用来找回边角相关的条目——仍有一条底线,不是无门槛。
86
+ - **不选时**:先按严格跑;严格档一条都没命中、且确实有条目被门槛挡掉时,自动用宽松档补跑一次,
87
+ 结果会标明「已自动放宽门槛」。门槛挡掉的空与真的没有,面板和结果里的数字能区分开。
156
88
 
157
89
  ## 配置、数据与提示词
158
90
 
159
- - **数据目录**:默认 `~/.avantf`。记忆库、知识库(含 `knowledge/docs` 下的受管文档副本)与模型缓存
160
- 都在其中。
91
+ - **数据目录**:默认 `~/.avantf`。记忆库、知识库(含受管文档副本)与模型缓存都在其中;
92
+ 环境变量 `AVANTF_HOME` 可以改变它。
161
93
  - **配置**:`~/.avantf/configs/` 下的三份 YAML —— `common.yaml`(公共项,缺失时按注释模板自动创建)、
162
- `memory.yaml`、`knowledge.yaml`;只写你要改的项,其余取内置默认。`dataHome` 也可以写在 profile 的
163
- config 里,但环境变量 `AVANTF_HOME` 会压过它。
164
- - **提示词文件**:`~/.avantf/prompts/mem-memory-usage.md`、`mem-knowledge-usage.md`、`mem-kb-edit.md`。
165
- 缺失或为空时插件写入默认内容,**只在启动时读一次**,改完重启生效。正文可以**按领域改写**——内置默认
166
- 刻意不出现任何领域名词(它同时服务开发、投研、日常等场景),只给判据与"事实型 / 规则型"两种形状:
94
+ `memory.yaml`、`knowledge.yaml`。只写你要改的项,其余取内置默认。
95
+ - **提示词文件**:`~/.avantf/prompts/` 下的 `mem-memory-usage.md`、`mem-knowledge-usage.md`、
96
+ `mem-kb-edit.md`。缺失或为空时插件写入默认内容,**只在启动时读一次**,改完重启生效。
97
+ 可以按自己的领域改写:内置默认刻意不出现任何领域名词,只给判据与两种形状——
167
98
  - 事实型:`用户不吃香菜;订餐默认备注不要香菜。`(谁 / 什么范围 / 偏好或要求或术语是什么)
168
- - 规则型:`只有「注销式回购」减少总股本,库存股回购不减少——比较 EPS 影响时要区分。`(当……时,应……,附条件与例外)
169
- - 反例(不该写成事实):一次调查的过程叙述、数据表、命令与链接——依据属于知识库文档,记忆里只留**结论 + 位置**
170
- (路径 / 来源 / 名称);敏感信息同理,只记存放位置,不记明文。
171
- - **自定义过提示词文件的话,升级时需手动补**:提示词**文件优先于内置正文**(文件存在且非空时内置正文完全不参与渲染)。
172
- 0.6.0 起内置正文多了三条行为口径,若你改过 `mem-memory-usage.md`,请把下面三行追加到你的文件末尾;没改过则无需操作:
173
- ```text
174
- 记忆是过去的记录、不是当下指令,无关时可完全忽略。
175
- 同一主题用 `update` 纠正并附核实依据,不同事实新增。
176
- 不要把 `recall` 结果原样回记。
177
- ```
178
- - **可选:中文时间窗检索腿**(0.6.0 起,**默认关闭**)。它在查询含中文时间表达时,把**事件时间**(写入时用
179
- `event_date` 给出)落在窗口内的活跃事实作为额外一条腿参与融合——只增加候选、不删已有命中,也不会回退到写入时间。
180
- 在 `~/.avantf/configs/common.yaml` 打开:
181
- ```yaml
182
- retriever:
183
- time_window: true
184
- ```
185
- 打开前先看 `mem_admin stats` 的 `validity.coverage`(带事件时间的事实占比):覆盖率接近 0 时这条腿等于没开。
186
-
187
- - **语义模型**:默认从 `hf-mirror.com` 下载并缓存,也可指向本地模型目录离线运行。
188
- - **升级前先备份 `~/.avantf`**:数据库迁移是单向的,用旧版本打开新库会被明确拒绝。
189
-
190
- ## 升级 / 卸载 / 换版本
99
+ - 规则型:`只有「注销式回购」减少总股本,库存股回购不减少——比较 EPS 影响时要区分。`
100
+ - 不适合写进记忆:一次调查的过程叙述、数据表、命令与链接——依据归知识库文档,记忆里只留
101
+ **结论 + 位置**(路径 / 来源 / 名称);敏感信息同理,只记存放位置,不记明文。
102
+ - **升级前建议先备份 `~/.avantf`**:数据库迁移是单向的,用旧版本打开新库会被明确拒绝。
103
+
104
+ ## 升级 / 卸载
191
105
 
192
106
  ```bash
193
- # 升级(或重装)到某个版本:同一条命令,换版本号即可
107
+ # 升级:换版本号重装同一个包
194
108
  dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
195
109
 
196
- # 卸载:先删掉 profile 依赖(这一步同时会取消该组合包的选中)
110
+ # 卸载
197
111
  dsh plugin --profile <PROFILE> remove @avantf/dsh-mem
198
- # 再清掉挂载项:用组合包装的,包名已随上一步从 profile 的 `dsh.profile.bundles` 里去掉,无需再动;
199
- # 自己手写过那一行的,删掉 ~/.dsh/profiles/<PROFILE>/cordis.patch.yml 里那段 `- id: avantf-mem`
200
- #(该文件顶层必须是 YAML 数组:删空就写 `[]`,否则 dsh 启动会拒绝加载)。
201
- # DSH Desktop 的「插件」页上卸载会连着取消选中与依赖一起做。
202
112
  ```
203
113
 
204
- 两者都需要重启 dsh 才生效。卸载**不动 `~/.avantf`**:记忆库、知识库、模型缓存全部保留。
114
+ 两者都要重启 dsh 生效。卸载**不动 `~/.avantf`**:记忆库、知识库、模型缓存全部保留。DSH Desktop 在
115
+ 「插件」页上卸载。
205
116
 
206
117
  ## 环境要求
207
118
 
208
- - **Node `>=22.15.0 <23 || >=23.11.0`**、**pnpm**。记忆与知识两个库都建在运行时自带的 `node:sqlite`
209
- 上,这个区间是它的两个下限的交集:模块免 flag(22.13 / 23.4)与"忽略语句未用到的命名参数"
210
- (22.15 / 23.11)。低于该下限(或该构建缺 FTS5)时插件照常挂载,但 8 个工具都回
211
- `memory unavailable` 并给出原因,不会拖垮宿主启动
212
- - `nodejieba` / `hnswlib-node` / `@huggingface/transformers` 都可选:缺失时分别降级为正则抽取 /
213
- numpy 向量库 / 纯词法检索
214
- - 插件自包含:检索与知识引擎在构建时已内联进产物,安装不需要额外的家族包。宿主提供的 peer 有两个:
215
- 底座 `@avantf/dsh-plugin-base`(安装时随插件一起装)与 `zod`(宿主的 dsh 自带那份)
216
-
217
- ## 从源码构建
218
-
219
- 构建只对着**已安装的全局 dsh**(`npm i -g @deepseek-ai/dsh`),不需要 harness 源码;在仓库根执行:
220
-
221
- ```bash
222
- pnpm install
223
- pnpm build:dsh mem # 插件两半(tsc + tsdown → lib/index.js + lib/client.js),随后自动跑挂载冒烟
224
- pnpm pack:plugin:mem # 打包成"用户安装的那一个包"(断言引擎已内联、依赖版本已落成真实范围)
225
- ```
119
+ - **Node `>=22.15.0 <23 || >=23.11.0`**、**pnpm**
120
+ - 可选的原生加速件(分词 / 向量索引 / 本地嵌入模型)缺失时只会降低检索质量,不影响使用
226
121
 
227
122
  ## 许可
228
123
 
@@ -13,7 +13,7 @@
13
13
  * `ToolSpec.key` is typed `string` (not a literal union), so exhaustiveness cannot be checked at
14
14
  * compile time — the spec assertion is the gate.
15
15
  */
16
- import type { KbAddRequest } from '../mem-contract/index.js';
16
+ import type { KbAddRequest, ToolEnvelope, ToolSpec } from '../mem-contract/index.js';
17
17
  import type { AvantfRuntime } from './runtime.js';
18
18
  /**
19
19
  * The default page for a model-facing `kb_list`.
@@ -49,3 +49,21 @@ export declare function supportsToolKey(key: string): boolean;
49
49
  * both adapters route the throw through their structured error envelope.
50
50
  */
51
51
  export declare function dispatchToolKey(rt: AvantfRuntime, key: string, args: Record<string, unknown>): Promise<unknown>;
52
+ /**
53
+ * The ONE model-facing boundary: validate against the contract spec, dispatch the key, and shape the
54
+ * result into the agent envelope.
55
+ *
56
+ * Every model-facing surface shares this — the DSH tool runner (`@avantf/dsh-mem`) and the MCP
57
+ * server — because the envelope is `ToolEnvelope`, a wire shape: an MCP caller and a DSH caller
58
+ * asking the same question must get the same fields. It used to live only in the plugin, so the MCP
59
+ * surface re-implemented it and the two could drift field by field.
60
+ *
61
+ * What stays in the CALLERS is everything AROUND the envelope: MCP's `textResult`/`isError` wrapper
62
+ * and the plugin's `present` view plus its Remote gateway. The Remote gateway is deliberately NOT
63
+ * routed through here (`mem/DESIGN.md` §239: it keeps `mem_admin`'s retention diagnostics and uses
64
+ * the wire error type, not `toolErr`).
65
+ *
66
+ * A validation failure answers `validationError` (named for the spec), an unknown key answers
67
+ * `toolErr`, and a throwing runtime call answers `toolErr` too — a raw exception never escapes.
68
+ */
69
+ export declare function runToolSpec(rt: AvantfRuntime, spec: ToolSpec, args: unknown): Promise<ToolEnvelope<unknown>>;
@@ -17,6 +17,7 @@ export * from './config/paths.js';
17
17
  export * from './config/loader.js';
18
18
  export * from './modelBootstrap.js';
19
19
  export * from './dispatch.js';
20
+ export * from './trustHeartbeat.js';
20
21
  export * from './runtime.js';
21
22
  export * from './store/memory.js';
22
23
  export * from './store/knowledge.js';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The trust heartbeat: the interval beat that advances the active-day clock and performs the
3
+ * settle / forget / idle / purge sweeps (TRUST_MODEL.md §5), shared by the DSH plugin and the MCP
4
+ * server.
5
+ *
6
+ * The two surfaces used to install it separately, and the copies drifted in exactly the places a
7
+ * copy drifts: the wording of the failure line and the work each beat does after the tick. The tick
8
+ * itself — the one thing that MUST be identical, because it is the trust clock — is what lives
9
+ * here. The differences are data: {@link TrustHeartbeatOptions.onBeat} is the extra work (the plugin
10
+ * sweeps entities and retries the vector migration), {@link TrustHeartbeatOptions.tickFailed} is how
11
+ * that surface names a failed sweep.
12
+ *
13
+ * `heartbeatMinutes <= 0` is the caller's decision, not this function's: a surface that disables the
14
+ * beat also decides whether it logs the interval line.
15
+ *
16
+ * Zero dependencies beyond `setInterval` so it stays in the engine (the plugin's index and the MCP
17
+ * entry both consume it).
18
+ */
19
+ /** A single beat's worth of the trust clock, plus the two per-surface differences. */
20
+ export interface TrustHeartbeatOptions {
21
+ /** The store whose active-day clock advances on presence. */
22
+ readonly memory: {
23
+ trustTick(): void;
24
+ };
25
+ /** Where a failed sweep is reported. Never allowed to make the beat itself throw. */
26
+ readonly logger: {
27
+ warn(message: string): void;
28
+ };
29
+ /** Minutes between beats. The caller does not install the beat unless this is positive. */
30
+ readonly heartbeatMinutes: number;
31
+ /** Extra per-beat work, run after the tick and OUTSIDE its catch (a failure here is the caller's). */
32
+ readonly onBeat?: () => void;
33
+ /**
34
+ * How this surface names a failed tick, given the caught value's message. Optional so the default
35
+ * (`trust tick failed: …`) holds for a caller that has no opinion.
36
+ */
37
+ readonly tickFailed?: (message: string) => string;
38
+ }
39
+ /**
40
+ * Install the heartbeat and return its disposer.
41
+ *
42
+ * A `trustTick` that throws is warned about and swallowed: a failed sweep must never take the host
43
+ * or the server down, and it must not be invisible either — a trust clock that stopped advancing
44
+ * shows up much later as facts that never settle, forget or purge. The timer is `unref`'d where the
45
+ * runtime supports it, so it never holds a process open by itself.
46
+ */
47
+ export declare function installTrustHeartbeat(options: TrustHeartbeatOptions): () => void;
package/lib/index.js CHANGED
@@ -1325,6 +1325,170 @@ function promptTextWarnings(sections) {
1325
1325
  return out;
1326
1326
  }
1327
1327
  //#endregion
1328
+ //#region lib/types/interface_gate.js
1329
+ /** Read one member without letting a hostile module's getter escape. */
1330
+ function member(module, name) {
1331
+ try {
1332
+ return module?.[name];
1333
+ } catch {
1334
+ return;
1335
+ }
1336
+ }
1337
+ function cannotTell(reason) {
1338
+ return {
1339
+ status: "cannot-tell",
1340
+ reason
1341
+ };
1342
+ }
1343
+ /**
1344
+ * Whether the base's shared capabilities may be used under `verdict`.
1345
+ *
1346
+ * The ONE mapping from the gate's verdict to the loader's decision, exported so it is asserted
1347
+ * directly and the loader and its test cannot drift: only `incompatible` withholds the base;
1348
+ * `cannot-tell` does not, because it is not a negative answer. A `false` here is still a DEGRADATION
1349
+ * (own prompt defaults, gate skipped, legacy provisioning), never a refused mount.
1350
+ */
1351
+ function baseIsUsable(verdict) {
1352
+ return verdict.status !== "incompatible";
1353
+ }
1354
+ /**
1355
+ * Where the build's bake record sits when no explicit URL is given: beside the built ENTRY
1356
+ * (`lib/index.js`), which is where `scripts/link-envinit.mjs` writes it. A per-file `tsc` emit of
1357
+ * this module lives one directory below that entry and looks one level up; the first readable record
1358
+ * wins. Under `vitest` (running from `src/`) neither exists, which is the correct "not baked".
1359
+ */
1360
+ const BAKED_CANDIDATES = ["./interface-version.json", "../interface-version.json"];
1361
+ /**
1362
+ * Read this artifact's baked requirement through the loaded base's own reader.
1363
+ * @param read - the base's `readInterfaceRequirement`, taken off the loaded module.
1364
+ * @param bakedUrl - test seam: read EXACTLY this record instead of walking {@link BAKED_CANDIDATES}.
1365
+ */
1366
+ function requirementOf(read, bakedUrl) {
1367
+ const candidates = bakedUrl === void 0 ? BAKED_CANDIDATES.map((relative) => new URL(relative, import.meta.url)) : [bakedUrl];
1368
+ for (const url of candidates) {
1369
+ let requirement;
1370
+ try {
1371
+ requirement = read(url);
1372
+ } catch {
1373
+ continue;
1374
+ }
1375
+ const version = requirement?.interfaceVersion;
1376
+ if (typeof version === "number" && Number.isInteger(version) && version > 0) return { interfaceVersion: version };
1377
+ }
1378
+ }
1379
+ /**
1380
+ * Ask the loaded base whether it implements the interface generation this artifact was built for.
1381
+ *
1382
+ * @param module - the loaded base module (only `checkInterface` / `readInterfaceRequirement` are
1383
+ * touched, each through a guarded read).
1384
+ * @param bakedUrl - test seam: the bake record to read; production leaves it undefined.
1385
+ * @returns a verdict that is never `incompatible` unless BOTH sides were readable and differ, and
1386
+ * never a throw.
1387
+ */
1388
+ function interfaceVerdict(module, bakedUrl) {
1389
+ const check = member(module, "checkInterface");
1390
+ const read = member(module, "readInterfaceRequirement");
1391
+ if (typeof check !== "function" || typeof read !== "function") return cannotTell("the loaded base has no interface gate (checkInterface / readInterfaceRequirement missing) — it predates the runtime interface contract, so which generation it implements cannot be told");
1392
+ const requirement = requirementOf(read, bakedUrl);
1393
+ if (requirement === void 0) return cannotTell("this build has no baked interface requirement (lib/interface-version.json is missing or malformed), so the runtime interface gate cannot run");
1394
+ let verdict;
1395
+ try {
1396
+ verdict = check(requirement.interfaceVersion, module);
1397
+ } catch {
1398
+ return cannotTell("the loaded base threw while judging the interface generation");
1399
+ }
1400
+ const status = verdict?.status;
1401
+ if (status !== "ok" && status !== "incompatible" && status !== "cannot-tell") return cannotTell("the loaded base returned no usable interface verdict");
1402
+ return verdict;
1403
+ }
1404
+ /** Emit a warning without ever letting the logger turn it into a crash. */
1405
+ function warn$1(log, message) {
1406
+ try {
1407
+ log?.warn(message);
1408
+ } catch {}
1409
+ }
1410
+ function reasonOf$1(error) {
1411
+ return error instanceof Error ? error.message : String(error);
1412
+ }
1413
+ /**
1414
+ * Run the runtime interface gate and answer whether the loaded base's shared capabilities may be
1415
+ * used.
1416
+ *
1417
+ * `false` is a DEGRADATION the caller answers by taking its own full-degradation route (no runtime,
1418
+ * own prompt defaults, the compatibility gate skipped, legacy provisioning) — never a refused mount,
1419
+ * and never a throw. `cannot-tell` warns and answers `true`: "cannot tell" is not a negative answer,
1420
+ * so the base is used normally. The verdict's own reason is the base's; only the surrounding sentence
1421
+ * is the caller's ({@link DegradeWords}).
1422
+ */
1423
+ function gateOrDegrade(module, log, words) {
1424
+ const verdict = interfaceVerdict(module);
1425
+ if (!baseIsUsable(verdict)) {
1426
+ warn$1(log, `${words.prefix} WARNING — interface: ${verdict.reason ?? "the loaded base implements another interface generation"}; the base's shared capabilities are NOT used (${words.withheld}) and the plugin mounts anyway`);
1427
+ return false;
1428
+ }
1429
+ if (verdict.status === "cannot-tell") warn$1(log, `${words.prefix} WARNING — interface: ${verdict.reason ?? "the interface generation cannot be told"}; using the loaded base anyway ("cannot tell" is never "incompatible")`);
1430
+ return true;
1431
+ }
1432
+ /**
1433
+ * The guard that makes a loader's "never throws" true: every failure is one warning plus
1434
+ * `undefined`. The caller owns the sentence's tail (`words`) and its own cache discipline — a
1435
+ * FAILED load is not cached, so this must return `undefined` rather than reject (see
1436
+ * {@link createLoadCache}).
1437
+ */
1438
+ async function loadGuarded(options, loadOnce, log, words) {
1439
+ try {
1440
+ return await loadOnce(options);
1441
+ } catch (error) {
1442
+ warn$1(log, `${words.prefix} WARNING — environment initialisation failed (${reasonOf$1(error)}); ${words.loadFailure}`);
1443
+ return;
1444
+ }
1445
+ }
1446
+ /**
1447
+ * Wrap a guarded loader in the cache discipline both startups share: the first call runs it, every
1448
+ * later and concurrent call shares its result, and a FAILED load is NOT cached. A failure is "cannot
1449
+ * tell", not a verdict — and it may be transient (offline at boot, a half-written install) — so a
1450
+ * profile reload, or a second instance in the same process, retries instead of replaying the first
1451
+ * failure for the whole process lifetime.
1452
+ */
1453
+ function createLoadCache(guarded) {
1454
+ let loaded;
1455
+ let loading;
1456
+ return {
1457
+ load(options) {
1458
+ if (loaded !== void 0) return Promise.resolve(loaded);
1459
+ if (loading !== void 0) return loading;
1460
+ loading = guarded(options).then((runtime) => {
1461
+ if (runtime === void 0) loading = void 0;
1462
+ else loaded = runtime;
1463
+ return runtime;
1464
+ }, () => {
1465
+ loading = void 0;
1466
+ });
1467
+ return loading;
1468
+ },
1469
+ clear() {
1470
+ loaded = void 0;
1471
+ loading = void 0;
1472
+ }
1473
+ };
1474
+ }
1475
+ /**
1476
+ * Whether a Cordis context is still alive.
1477
+ *
1478
+ * Cordis clears `fiber.uid` on disposal, and every later context call then throws
1479
+ * `INACTIVE_EFFECT`. `apply` awaits environment preparation, so a profile reload or unload can land
1480
+ * inside that window and half-register a plugin (the Remote face cannot be rolled back). A fiberless
1481
+ * stub counts as active: an unknown shape is "cannot tell", never "disposed".
1482
+ *
1483
+ * Generic Cordis liveness rather than interface-gate knowledge — but the two plugins' startup paths
1484
+ * share it byte-for-byte, so it lives with their one shared shim, where the two readings of "is my
1485
+ * context alive" cannot drift.
1486
+ */
1487
+ function stillActive(ctx) {
1488
+ const fiber = ctx?.fiber;
1489
+ return fiber === void 0 || fiber.uid !== null;
1490
+ }
1491
+ //#endregion
1328
1492
  //#region lib/types/hints.js
1329
1493
  /**
1330
1494
  * The plugin's CONDITIONAL hint: ONE line, contributed only when either store already holds
@@ -3688,6 +3852,79 @@ async function dispatchToolKey(rt, key, args) {
3688
3852
  if (dispatch === void 0) throw new Error(`unknown tool key ${key}`);
3689
3853
  return dispatch(rt, args);
3690
3854
  }
3855
+ /**
3856
+ * The ONE model-facing boundary: validate against the contract spec, dispatch the key, and shape the
3857
+ * result into the agent envelope.
3858
+ *
3859
+ * Every model-facing surface shares this — the DSH tool runner (`@avantf/dsh-mem`) and the MCP
3860
+ * server — because the envelope is `ToolEnvelope`, a wire shape: an MCP caller and a DSH caller
3861
+ * asking the same question must get the same fields. It used to live only in the plugin, so the MCP
3862
+ * surface re-implemented it and the two could drift field by field.
3863
+ *
3864
+ * What stays in the CALLERS is everything AROUND the envelope: MCP's `textResult`/`isError` wrapper
3865
+ * and the plugin's `present` view plus its Remote gateway. The Remote gateway is deliberately NOT
3866
+ * routed through here (`mem/DESIGN.md` §239: it keeps `mem_admin`'s retention diagnostics and uses
3867
+ * the wire error type, not `toolErr`).
3868
+ *
3869
+ * A validation failure answers `validationError` (named for the spec), an unknown key answers
3870
+ * `toolErr`, and a throwing runtime call answers `toolErr` too — a raw exception never escapes.
3871
+ */
3872
+ async function runToolSpec$1(rt, spec, args) {
3873
+ const parsed = spec.input.safeParse(args ?? {});
3874
+ if (!parsed.success) return validationError(spec.name, parsed.error.issues);
3875
+ if (!supportsToolKey(spec.key)) return toolErr(`unknown tool ${spec.key}`);
3876
+ try {
3877
+ const value = await dispatchToolKey(rt, spec.key, parsed.data);
3878
+ return toolOk(modelFacingToolResult(spec.key, parsed.data.action, value));
3879
+ } catch (error) {
3880
+ return toolErr(error);
3881
+ }
3882
+ }
3883
+ //#endregion
3884
+ //#region ../core/lib/trustHeartbeat.js
3885
+ /**
3886
+ * The trust heartbeat: the interval beat that advances the active-day clock and performs the
3887
+ * settle / forget / idle / purge sweeps (TRUST_MODEL.md §5), shared by the DSH plugin and the MCP
3888
+ * server.
3889
+ *
3890
+ * The two surfaces used to install it separately, and the copies drifted in exactly the places a
3891
+ * copy drifts: the wording of the failure line and the work each beat does after the tick. The tick
3892
+ * itself — the one thing that MUST be identical, because it is the trust clock — is what lives
3893
+ * here. The differences are data: {@link TrustHeartbeatOptions.onBeat} is the extra work (the plugin
3894
+ * sweeps entities and retries the vector migration), {@link TrustHeartbeatOptions.tickFailed} is how
3895
+ * that surface names a failed sweep.
3896
+ *
3897
+ * `heartbeatMinutes <= 0` is the caller's decision, not this function's: a surface that disables the
3898
+ * beat also decides whether it logs the interval line.
3899
+ *
3900
+ * Zero dependencies beyond `setInterval` so it stays in the engine (the plugin's index and the MCP
3901
+ * entry both consume it).
3902
+ */
3903
+ /** The generic one-line reason for a caught value. */
3904
+ function messageOf$1(error) {
3905
+ return error instanceof Error ? error.message : String(error);
3906
+ }
3907
+ /**
3908
+ * Install the heartbeat and return its disposer.
3909
+ *
3910
+ * A `trustTick` that throws is warned about and swallowed: a failed sweep must never take the host
3911
+ * or the server down, and it must not be invisible either — a trust clock that stopped advancing
3912
+ * shows up much later as facts that never settle, forget or purge. The timer is `unref`'d where the
3913
+ * runtime supports it, so it never holds a process open by itself.
3914
+ */
3915
+ function installTrustHeartbeat(options) {
3916
+ const timer = setInterval(() => {
3917
+ try {
3918
+ options.memory.trustTick();
3919
+ } catch (error) {
3920
+ options.logger.warn((options.tickFailed ?? ((message) => `trust tick failed: ${message}`))(messageOf$1(error)));
3921
+ }
3922
+ options.onBeat?.();
3923
+ }, options.heartbeatMinutes * 6e4);
3924
+ const unref = timer.unref;
3925
+ if (typeof unref === "function") unref.call(timer);
3926
+ return () => clearInterval(timer);
3927
+ }
3691
3928
  //#endregion
3692
3929
  //#region ../retrieval-core/lib/representation.js
3693
3930
  /**
@@ -16419,6 +16656,12 @@ function cannotDetermine(error, log, prefix) {
16419
16656
  * Run the gate. Call this FIRST in `apply()`: on a refusal nothing has been registered yet, so
16420
16657
  * "do not load" costs nothing to unwind. Never throws — not even with a throwing logger, because
16421
16658
  * "cannot tell" is not "incompatible".
16659
+ *
16660
+ * The check itself is the BASE's `provision` (gather + verdict + logging, one call). The local
16661
+ * fallback stays on purpose (option A): the interface-generation axis cannot gate this dependency
16662
+ * (same-generation bases never read as `incompatible`), and raising the peer floor would degrade
16663
+ * every host on a base that predates the fix — so a `provision` that cannot be performed still
16664
+ * leaves the plugin loading.
16422
16665
  * @param ctx - the plugin's context.
16423
16666
  * @param log - the plugin's logger.
16424
16667
  * @param compat - the loaded gate runtime (see `src/envinit.ts`).
@@ -16427,22 +16670,11 @@ function cannotDetermine(error, log, prefix) {
16427
16670
  * @returns the verdict `apply` branches on.
16428
16671
  */
16429
16672
  function provision(ctx, log, compat, spec = compat.spec) {
16430
- let evidence;
16431
- try {
16432
- evidence = compat.module.gatherEvidence(ctx, spec);
16433
- } catch (error) {
16434
- return cannotDetermine(error, log, compat.prefix);
16435
- }
16436
- let verdict;
16437
16673
  try {
16438
- verdict = compat.module.verdictOf(evidence);
16674
+ return { verdict: compat.module.provision(ctx, log, spec) };
16439
16675
  } catch (error) {
16440
16676
  return cannotDetermine(error, log, compat.prefix);
16441
16677
  }
16442
- for (const line of verdict.lines) try {
16443
- log[line.level](line.message);
16444
- } catch {}
16445
- return { verdict };
16446
16678
  }
16447
16679
  /**
16448
16680
  * Keep one `/mem` command that reports a refusal.
@@ -16846,7 +17078,10 @@ async function loadFramework(options) {
16846
17078
  }
16847
17079
  /**
16848
17080
  * Read the range a plugin declares in the nearest `package.json`: walk up from `from`,
16849
- * preferring `peerDependencies` over `devDependencies`; `undefined` when none is found.
17081
+ * preferring `dependencies` over `peerDependencies` over `devDependencies`; `undefined` when none is
17082
+ * found. `dependencies` is the live declaration — the base ships as a normal dependency of the
17083
+ * plugins — while `peerDependencies` stays as the fallback for plugins built when the base was
17084
+ * declared a peer, and `devDependencies` covers a local checkout.
16850
17085
  */
16851
17086
  async function readDependencyRange(from = process.cwd()) {
16852
17087
  let current = from;
@@ -16876,7 +17111,7 @@ async function readDependencyRange(from = process.cwd()) {
16876
17111
  const parsed = JSON.parse(text);
16877
17112
  if (typeof parsed !== "object" || parsed === null) return void 0;
16878
17113
  const manifest = parsed;
16879
- return rangeOf(manifest["peerDependencies"]) ?? rangeOf(manifest["devDependencies"]);
17114
+ return rangeOf(manifest["dependencies"]) ?? rangeOf(manifest["peerDependencies"]) ?? rangeOf(manifest["devDependencies"]);
16880
17115
  } catch {
16881
17116
  return;
16882
17117
  }
@@ -16888,83 +17123,6 @@ function rangeOf(value) {
16888
17123
  return typeof range === "string" && range.trim() !== "" ? range : void 0;
16889
17124
  }
16890
17125
  //#endregion
16891
- //#region lib/types/interface_gate.js
16892
- /** Read one member without letting a hostile module's getter escape. */
16893
- function member(module, name) {
16894
- try {
16895
- return module?.[name];
16896
- } catch {
16897
- return;
16898
- }
16899
- }
16900
- function cannotTell(reason) {
16901
- return {
16902
- status: "cannot-tell",
16903
- reason
16904
- };
16905
- }
16906
- /**
16907
- * Whether the base's shared capabilities may be used under `verdict`.
16908
- *
16909
- * The ONE mapping from the gate's verdict to the loader's decision, exported so it is asserted
16910
- * directly and the loader and its test cannot drift: only `incompatible` withholds the base;
16911
- * `cannot-tell` does not, because it is not a negative answer. A `false` here is still a DEGRADATION
16912
- * (own prompt defaults, gate skipped, legacy provisioning), never a refused mount.
16913
- */
16914
- function baseIsUsable(verdict) {
16915
- return verdict.status !== "incompatible";
16916
- }
16917
- /**
16918
- * Where the build's bake record sits when no explicit URL is given: beside the built ENTRY
16919
- * (`lib/index.js`), which is where `scripts/link-envinit.mjs` writes it. A per-file `tsc` emit of
16920
- * this module lives one directory below that entry and looks one level up; the first readable record
16921
- * wins. Under `vitest` (running from `src/`) neither exists, which is the correct "not baked".
16922
- */
16923
- const BAKED_CANDIDATES = ["./interface-version.json", "../interface-version.json"];
16924
- /**
16925
- * Read this artifact's baked requirement through the loaded base's own reader.
16926
- * @param read - the base's `readInterfaceRequirement`, taken off the loaded module.
16927
- * @param bakedUrl - test seam: read EXACTLY this record instead of walking {@link BAKED_CANDIDATES}.
16928
- */
16929
- function requirementOf(read, bakedUrl) {
16930
- const candidates = bakedUrl === void 0 ? BAKED_CANDIDATES.map((relative) => new URL(relative, import.meta.url)) : [bakedUrl];
16931
- for (const url of candidates) {
16932
- let requirement;
16933
- try {
16934
- requirement = read(url);
16935
- } catch {
16936
- continue;
16937
- }
16938
- const version = requirement?.interfaceVersion;
16939
- if (typeof version === "number" && Number.isInteger(version) && version > 0) return { interfaceVersion: version };
16940
- }
16941
- }
16942
- /**
16943
- * Ask the loaded base whether it implements the interface generation this artifact was built for.
16944
- *
16945
- * @param module - the loaded base module (only `checkInterface` / `readInterfaceRequirement` are
16946
- * touched, each through a guarded read).
16947
- * @param bakedUrl - test seam: the bake record to read; production leaves it undefined.
16948
- * @returns a verdict that is never `incompatible` unless BOTH sides were readable and differ, and
16949
- * never a throw.
16950
- */
16951
- function interfaceVerdict(module, bakedUrl) {
16952
- const check = member(module, "checkInterface");
16953
- const read = member(module, "readInterfaceRequirement");
16954
- if (typeof check !== "function" || typeof read !== "function") return cannotTell("the loaded base has no interface gate (checkInterface / readInterfaceRequirement missing) — it predates the runtime interface contract, so which generation it implements cannot be told");
16955
- const requirement = requirementOf(read, bakedUrl);
16956
- if (requirement === void 0) return cannotTell("this build has no baked interface requirement (lib/interface-version.json is missing or malformed), so the runtime interface gate cannot run");
16957
- let verdict;
16958
- try {
16959
- verdict = check(requirement.interfaceVersion, module);
16960
- } catch {
16961
- return cannotTell("the loaded base threw while judging the interface generation");
16962
- }
16963
- const status = verdict?.status;
16964
- if (status !== "ok" && status !== "incompatible" && status !== "cannot-tell") return cannotTell("the loaded base returned no usable interface verdict");
16965
- return verdict;
16966
- }
16967
- //#endregion
16968
17126
  //#region lib/types/envinit.js
16969
17127
  /**
16970
17128
  * Environment initialisation for the memory plugin, through the family base
@@ -17224,8 +17382,22 @@ function modelItem(framework, model) {
17224
17382
  policy: { timeoutMs: MODEL_REQUEST_TIMEOUT_MS }
17225
17383
  };
17226
17384
  }
17227
- let loaded;
17228
- let loading;
17385
+ /**
17386
+ * The plugin-specific halves of the shared loader's sentences: mem says the FRAMEWORK is skipped and
17387
+ * names the legacy provisioning path among the withheld capabilities. The mechanism is the shim's
17388
+ * (`interface_gate.ts`), the wording is this plugin's.
17389
+ */
17390
+ const DEGRADE_WORDS = {
17391
+ prefix: OWN_PREFIX,
17392
+ loadFailure: "the framework is SKIPPED and the plugin will mount anyway",
17393
+ withheld: "own prompt defaults, gate skipped, legacy provisioning"
17394
+ };
17395
+ /**
17396
+ * The loaded runtime, cached once per process through the shared cache discipline: a FAILED load is
17397
+ * NOT cached, so a profile reload retries instead of replaying the first failure for the whole
17398
+ * process lifetime.
17399
+ */
17400
+ const cache = createLoadCache((options) => loadGuarded(options, loadOnce, options.log, DEGRADE_WORDS));
17229
17401
  /**
17230
17402
  * Ensure the framework, then the compatibility base, then dispatch the expensive resources.
17231
17403
  *
@@ -17235,32 +17407,11 @@ let loading;
17235
17407
  * base still returns a runtime, with `compat: undefined`: the mount must continue, and the resource
17236
17408
  * items are still worth dispatching.
17237
17409
  *
17238
- * The result is cached per process; a FAILED load is not, so a profile reload retries instead of
17239
- * replaying the first failure for the whole process lifetime.
17240
- *
17241
17410
  * @param options - logger, family root, download policy and the test seams.
17242
17411
  * @returns the loaded runtime, or `undefined` when the framework itself is unavailable.
17243
17412
  */
17244
- async function loadEnvinit(options = {}) {
17245
- if (loaded !== void 0) return loaded;
17246
- if (loading !== void 0) return loading;
17247
- loading = loadGuarded(options).then((runtime) => {
17248
- if (runtime === void 0) loading = void 0;
17249
- else loaded = runtime;
17250
- return runtime;
17251
- }, () => {
17252
- loading = void 0;
17253
- });
17254
- return loading;
17255
- }
17256
- /** The guard that makes "never throws" true: every failure is a warning plus `undefined`. */
17257
- async function loadGuarded(options) {
17258
- try {
17259
- return await loadOnce(options);
17260
- } catch (error) {
17261
- warn(options.log, `${OWN_PREFIX} WARNING — environment initialisation failed (${reasonOf(error)}); the framework is SKIPPED and the plugin will mount anyway`);
17262
- return;
17263
- }
17413
+ function loadEnvinit(options = {}) {
17414
+ return cache.load(options);
17264
17415
  }
17265
17416
  /** The body of {@link loadEnvinit}; the cache above makes it run at most once per process. */
17266
17417
  async function loadOnce(options) {
@@ -17280,14 +17431,7 @@ async function loadOnce(options) {
17280
17431
  warn(options.log, `${OWN_PREFIX} WARNING — ${FRAMEWORK_PACKAGE} could not be made available; the plugin falls back to its own provisioning and mounts anyway`);
17281
17432
  return;
17282
17433
  }
17283
- {
17284
- const verdict = interfaceVerdict(framework);
17285
- if (!baseIsUsable(verdict)) {
17286
- warn(options.log, `${OWN_PREFIX} WARNING — interface: ${verdict.reason ?? "the loaded base implements another interface generation"}; the base's shared capabilities are NOT used (own prompt defaults, gate skipped, legacy provisioning) and the plugin mounts anyway`);
17287
- return;
17288
- }
17289
- if (verdict.status === "cannot-tell") warn(options.log, `${OWN_PREFIX} WARNING — interface: ${verdict.reason ?? "the interface generation cannot be told"}; using the loaded base anyway ("cannot tell" is never "incompatible")`);
17290
- }
17434
+ if (!gateOrDegrade(framework, options.log, DEGRADE_WORDS)) return void 0;
17291
17435
  const disposers = [];
17292
17436
  let compat;
17293
17437
  try {
@@ -17347,8 +17491,7 @@ async function loadOnce(options) {
17347
17491
  for (const instance of disposers.splice(0)) try {
17348
17492
  instance.dispose();
17349
17493
  } catch {}
17350
- loaded = void 0;
17351
- loading = void 0;
17494
+ cache.clear();
17352
17495
  }
17353
17496
  };
17354
17497
  }
@@ -17899,15 +18042,7 @@ const ENTITY_SWEEP_PER_HEARTBEAT = 2e3;
17899
18042
  /** Minimum spacing between corpus drift checks (`tools/result` fires for EVERY tool). */
17900
18043
  const RECONCILE_MIN_INTERVAL_MS = 2e3;
17901
18044
  async function runToolSpec(spec, rt, args) {
17902
- const parsed = spec.input.safeParse(args ?? {});
17903
- if (!parsed.success) return validationError(spec.name, parsed.error.issues);
17904
- if (!supportsToolKey(spec.key)) return toolErr(`unknown tool ${spec.key}`);
17905
- try {
17906
- const value = await dispatchToolKey(rt, spec.key, parsed.data);
17907
- return toolOk(modelFacingToolResult(spec.key, parsed.data.action, value));
17908
- } catch (error) {
17909
- return toolErr(error);
17910
- }
18045
+ return await runToolSpec$1(rt, spec, args);
17911
18046
  }
17912
18047
  function present(spec, args) {
17913
18048
  return {
@@ -18261,19 +18396,6 @@ function hostLogger(ctx) {
18261
18396
  };
18262
18397
  }
18263
18398
  /**
18264
- * Whether this plugin's fiber is still alive after the environment await.
18265
- *
18266
- * The environment gate can take the whole startup budget (15 s on a first run) and the framework may
18267
- * even fetch the gate's base during it. A profile reload or unload landing inside that window clears
18268
- * the fiber's uid, after which every Cordis call throws `INACTIVE_EFFECT` — the plugin would then
18269
- * half-register (the Remote face cannot be rolled back) instead of stopping cleanly. An unknown
18270
- * shape is "cannot tell", so only an explicitly cleared uid counts as disposed.
18271
- */
18272
- function stillActive(ctx) {
18273
- const fiber = ctx.fiber;
18274
- return fiber === void 0 || fiber.uid !== null;
18275
- }
18276
- /**
18277
18399
  * Declare the expensive resources through the family framework, warm the tokenizer and the embedding
18278
18400
  * model, and continue initialisation as each item settles.
18279
18401
  *
@@ -18429,19 +18551,18 @@ async function apply(ctx, config) {
18429
18551
  });
18430
18552
  const heartbeatMinutes = rt.config.common.trust.presence.heartbeat_minutes;
18431
18553
  if (heartbeatMinutes > 0) ctx.effect(() => {
18432
- const timer = setInterval(() => {
18433
- try {
18434
- rt.memory.trustTick();
18435
- } catch (error) {
18436
- logger.warn(`trust tick failed: ${error instanceof Error ? error.message : String(error)}`);
18437
- }
18438
- sweepEntities();
18439
- vectorMigration.start();
18440
- }, heartbeatMinutes * 6e4);
18441
- const unref = timer.unref;
18442
- if (typeof unref === "function") unref.call(timer);
18554
+ const stop = installTrustHeartbeat({
18555
+ memory: rt.memory,
18556
+ logger,
18557
+ heartbeatMinutes,
18558
+ onBeat: () => {
18559
+ sweepEntities();
18560
+ vectorMigration.start();
18561
+ },
18562
+ tickFailed: (message) => `trust tick failed: ${message}`
18563
+ });
18443
18564
  logger.info(`trust heartbeat: every ${heartbeatMinutes}min (active-day clock + lifecycle sweep)`);
18444
- return () => clearInterval(timer);
18565
+ return stop;
18445
18566
  });
18446
18567
  const promptDir = join(rt.config.home, "prompts");
18447
18568
  const kit = env?.kit;
@@ -1,4 +1,4 @@
1
1
  {
2
- "baseVersion": "0.4.0",
2
+ "baseVersion": "0.4.2",
3
3
  "interfaceVersion": 3
4
4
  }
@@ -3,7 +3,7 @@
3
3
  * @module bootstrap
4
4
  */
5
5
  /** The bootstrap version; the resolved framework must satisfy {@link supportedRange}. */
6
- export declare const VERSION = "0.4.0";
6
+ export declare const VERSION = "0.4.2";
7
7
  /**
8
8
  * The framework interval this bootstrap can launch.
9
9
  *
@@ -49,6 +49,9 @@ export declare function ensureFramework(options: EnsureFrameworkOptions): Promis
49
49
  export declare function loadFramework<T = unknown>(options: EnsureFrameworkOptions): Promise<T | undefined>;
50
50
  /**
51
51
  * Read the range a plugin declares in the nearest `package.json`: walk up from `from`,
52
- * preferring `peerDependencies` over `devDependencies`; `undefined` when none is found.
52
+ * preferring `dependencies` over `peerDependencies` over `devDependencies`; `undefined` when none is
53
+ * found. `dependencies` is the live declaration — the base ships as a normal dependency of the
54
+ * plugins — while `peerDependencies` stays as the fallback for plugins built when the base was
55
+ * declared a peer, and `devDependencies` covers a local checkout.
53
56
  */
54
57
  export declare function readDependencyRange(from?: string): Promise<string | undefined>;
@@ -123,9 +123,6 @@ interface EnvinitLoadOptions {
123
123
  * base still returns a runtime, with `compat: undefined`: the mount must continue, and the resource
124
124
  * items are still worth dispatching.
125
125
  *
126
- * The result is cached per process; a FAILED load is not, so a profile reload retries instead of
127
- * replaying the first failure for the whole process lifetime.
128
- *
129
126
  * @param options - logger, family root, download policy and the test seams.
130
127
  * @returns the loaded runtime, or `undefined` when the framework itself is unavailable.
131
128
  */
@@ -1 +1 @@
1
- {"version":3,"file":"envinit.d.ts","sourceRoot":"","sources":["../../src/envinit.ts"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAiB,oBAAoB,EAAe,MAAM,yBAAyB,CAAA;AAG/F,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAA;AAKvB,4FAA4F;AAC5F,KAAK,aAAa,GAAG,cAAc,yBAAyB,CAAC,CAAA;AAK7D;;;GAGG;AACH,eAAO,MAAM,WAAW,eAAe,CAAA;AACvC,eAAO,MAAM,UAAU,cAAc,CAAA;AAmDrC,2EAA2E;AAC3E,UAAU,aAAa;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,8FAA8F;IAC9F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAA;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EACV;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GACvF,SAAS,CAAA;CACd;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE;IACR,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,EACD,UAAU,EAAE,MAAM,GACjB,YAAY,CAAC,OAAO,CAAC,CAQvB;AAOD,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CAAA;IACvC,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAA;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACnE;;;;;;OAMG;IACH,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,IAAI,CAAA;IAC9F,wEAAwE;IACxE,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,uCAAuC;AACvC,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAA;IAC5B,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,mHAAmH;IACnH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CACrC;AA6HD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAkBvG"}
1
+ {"version":3,"file":"envinit.d.ts","sourceRoot":"","sources":["../../src/envinit.ts"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAiB,oBAAoB,EAAe,MAAM,yBAAyB,CAAA;AAG/F,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAA;AAKvB,4FAA4F;AAC5F,KAAK,aAAa,GAAG,cAAc,yBAAyB,CAAC,CAAA;AAK7D;;;GAGG;AACH,eAAO,MAAM,WAAW,eAAe,CAAA;AACvC,eAAO,MAAM,UAAU,cAAc,CAAA;AAmDrC,2EAA2E;AAC3E,UAAU,aAAa;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,8FAA8F;IAC9F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAA;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EACV;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GACvF,SAAS,CAAA;CACd;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE;IACR,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,EACD,UAAU,EAAE,MAAM,GACjB,YAAY,CAAC,OAAO,CAAC,CAQvB;AAOD,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CAAA;IACvC,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAA;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACnE;;;;;;OAMG;IACH,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,IAAI,CAAA;IAC9F,wEAAwE;IACxE,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,uCAAuC;AACvC,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAA;IAC5B,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,mHAAmH;IACnH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CACrC;AA8ID;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAEjG"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAQlD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,OAAO,EAAE,mBAAmB,EAAU,MAAM,kCAAkC,CAAA;AAE9E,OAAO,EAQL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAEhB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAeL,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,YAAY,EACjB,KAAK,cAAc,EAEnB,KAAK,oBAAoB,EAG1B,MAAM,sBAAsB,CAAA;AAqB7B,eAAO,MAAM,IAAI,eAAe,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAQ7E,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAA;AAEpB,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AACD,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAO3B,CAAA;AA6CF,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAOvC;;;;;;;;;;GAUG;AACH,qBAAa,gBAAiB,SAAQ,mBAAmB;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;gBACtB,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,aAAa;IAK3C;;;;;;;;;OASG;YACW,IAAI;IAclB;;;;OAIG;IAEH,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;IAMlF,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IAMnF,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAKhF;;;;;OAKG;IAEH,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAOpD,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAI5D;;;;OAIG;IAEG,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;IAUnF,qFAAqF;IAE/E,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IAUvE;;;;OAIG;IAEG,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;IAQvG;;;;OAIG;IAEG,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;IAUpG;;;;;OAKG;IAEG,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;CAiBpE;AAiID,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwYvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AASlD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,OAAO,EAAE,mBAAmB,EAAU,MAAM,kCAAkC,CAAA;AAE9E,OAAO,EASL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAEhB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAaL,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,YAAY,EACjB,KAAK,cAAc,EAEnB,KAAK,oBAAoB,EAG1B,MAAM,sBAAsB,CAAA;AAqB7B,eAAO,MAAM,IAAI,eAAe,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAQ7E,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAA;AAEpB,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AACD,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAO3B,CAAA;AAkCF,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAOvC;;;;;;;;;;GAUG;AACH,qBAAa,gBAAiB,SAAQ,mBAAmB;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;gBACtB,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,aAAa;IAK3C;;;;;;;;;OASG;YACW,IAAI;IAclB;;;;OAIG;IAEH,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;IAMlF,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IAMnF,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAKhF;;;;;OAKG;IAEH,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAOpD,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAI5D;;;;OAIG;IAEG,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;IAUnF,qFAAqF;IAE/E,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IAUvE;;;;OAIG;IAEG,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;IAQvG;;;;OAIG;IAEG,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;IAUpG;;;;;OAKG;IAEG,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;CAiBpE;AAmHD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyYvE"}
@@ -20,6 +20,13 @@
20
20
  * `envinit.ts`, never here. `base/plugin-base/test/interface_consumers.spec.ts` pins the two copies
21
21
  * against each other, so a divergence is a red test instead of two shims that drifted.
22
22
  *
23
+ * The loader SKELETON the two startups share lives here too: the once-per-process cache
24
+ * ({@link createLoadCache}), the guard that turns any throw into one warning
25
+ * ({@link loadGuarded}), and the gate→decision step ({@link gateOrDegrade}). Those sentences are
26
+ * observably NOT the same in the two plugins, so every shared function takes them as
27
+ * {@link DegradeWords}: the shim owns the mechanism, the caller owns the wording, and the two trees'
28
+ * `envinit.spec.ts` pin each plugin's own text verbatim.
29
+ *
23
30
  * @module @avantf/dsh-mem/interface_gate
24
31
  */
25
32
  import type { InterfaceVerdict } from '@avantf/dsh-plugin-base';
@@ -48,4 +55,67 @@ export declare function baseIsUsable(verdict: InterfaceVerdict): boolean;
48
55
  * never a throw.
49
56
  */
50
57
  export declare function interfaceVerdict(module: unknown, bakedUrl?: URL | string): InterfaceVerdict;
58
+ /** The logging surface the shared loader helpers need; both plugins' loggers satisfy it. */
59
+ export interface GateLogger {
60
+ warn(message: string): void;
61
+ }
62
+ /**
63
+ * The two sentences the shared loader may NOT own, because the plugins word them differently: mem
64
+ * says the FRAMEWORK is skipped and names legacy provisioning among the withheld capabilities,
65
+ * mission says the COMPATIBILITY GATE is skipped and names none. Neither is drift — both are read by
66
+ * an operator — so the caller passes them in and the shared code interpolates them verbatim.
67
+ */
68
+ export interface DegradeWords {
69
+ /** This module's warning token, so one grep finds every startup line: `envinit:` / `compat:`. */
70
+ readonly prefix: string;
71
+ /** The tail after "environment initialisation failed (...); " in the guarded-load failure line. */
72
+ readonly loadFailure: string;
73
+ /** The parenthetical after "the base's shared capabilities are NOT used (" in the incompatible line. */
74
+ readonly withheld: string;
75
+ }
76
+ /**
77
+ * Run the runtime interface gate and answer whether the loaded base's shared capabilities may be
78
+ * used.
79
+ *
80
+ * `false` is a DEGRADATION the caller answers by taking its own full-degradation route (no runtime,
81
+ * own prompt defaults, the compatibility gate skipped, legacy provisioning) — never a refused mount,
82
+ * and never a throw. `cannot-tell` warns and answers `true`: "cannot tell" is not a negative answer,
83
+ * so the base is used normally. The verdict's own reason is the base's; only the surrounding sentence
84
+ * is the caller's ({@link DegradeWords}).
85
+ */
86
+ export declare function gateOrDegrade(module: unknown, log: GateLogger | undefined, words: DegradeWords): boolean;
87
+ /**
88
+ * The guard that makes a loader's "never throws" true: every failure is one warning plus
89
+ * `undefined`. The caller owns the sentence's tail (`words`) and its own cache discipline — a
90
+ * FAILED load is not cached, so this must return `undefined` rather than reject (see
91
+ * {@link createLoadCache}).
92
+ */
93
+ export declare function loadGuarded<O, T>(options: O, loadOnce: (options: O) => Promise<T | undefined>, log: GateLogger | undefined, words: DegradeWords): Promise<T | undefined>;
94
+ /** The per-process result of a guarded load: `load` is idempotent, `clear` is for disposal. */
95
+ export interface LoadCache<O, T> {
96
+ load(options: O): Promise<T | undefined>;
97
+ /** Drop the result and the in-flight promise, so the next `load` runs again (unmount/reload). */
98
+ clear(): void;
99
+ }
100
+ /**
101
+ * Wrap a guarded loader in the cache discipline both startups share: the first call runs it, every
102
+ * later and concurrent call shares its result, and a FAILED load is NOT cached. A failure is "cannot
103
+ * tell", not a verdict — and it may be transient (offline at boot, a half-written install) — so a
104
+ * profile reload, or a second instance in the same process, retries instead of replaying the first
105
+ * failure for the whole process lifetime.
106
+ */
107
+ export declare function createLoadCache<O, T>(guarded: (options: O) => Promise<T | undefined>): LoadCache<O, T>;
108
+ /**
109
+ * Whether a Cordis context is still alive.
110
+ *
111
+ * Cordis clears `fiber.uid` on disposal, and every later context call then throws
112
+ * `INACTIVE_EFFECT`. `apply` awaits environment preparation, so a profile reload or unload can land
113
+ * inside that window and half-register a plugin (the Remote face cannot be rolled back). A fiberless
114
+ * stub counts as active: an unknown shape is "cannot tell", never "disposed".
115
+ *
116
+ * Generic Cordis liveness rather than interface-gate knowledge — but the two plugins' startup paths
117
+ * share it byte-for-byte, so it lives with their one shared shim, where the two readings of "is my
118
+ * context alive" cannot drift.
119
+ */
120
+ export declare function stillActive(ctx: unknown): boolean;
51
121
  //# sourceMappingURL=interface_gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interface_gate.d.ts","sourceRoot":"","sources":["../../src/interface_gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D,mGAAmG;AACnG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAA;CAC5C;AAkBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE/D;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,MAAM,GAAG,gBAAgB,CAuB3F"}
1
+ {"version":3,"file":"interface_gate.d.ts","sourceRoot":"","sources":["../../src/interface_gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D,mGAAmG;AACnG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAA;CAC5C;AAkBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE/D;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,MAAM,GAAG,gBAAgB,CAuB3F;AAED,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAeD;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,mGAAmG;IACnG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,wGAAwG;IACxG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAiBxG;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,CAAC,EACpC,OAAO,EAAE,CAAC,EACV,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,EAChD,GAAG,EAAE,UAAU,GAAG,SAAS,EAC3B,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAUxB;AAED,+FAA+F;AAC/F,MAAM,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IACxC,iGAAiG;IACjG,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAyBtG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGjD"}
@@ -84,6 +84,12 @@ export declare function runtimeFromCompat(module: CompatModule): CompatRuntime;
84
84
  * Run the gate. Call this FIRST in `apply()`: on a refusal nothing has been registered yet, so
85
85
  * "do not load" costs nothing to unwind. Never throws — not even with a throwing logger, because
86
86
  * "cannot tell" is not "incompatible".
87
+ *
88
+ * The check itself is the BASE's `provision` (gather + verdict + logging, one call). The local
89
+ * fallback stays on purpose (option A): the interface-generation axis cannot gate this dependency
90
+ * (same-generation bases never read as `incompatible`), and raising the peer floor would degrade
91
+ * every host on a base that predates the fix — so a `provision` that cannot be performed still
92
+ * leaves the plugin loading.
87
93
  * @param ctx - the plugin's context.
88
94
  * @param log - the plugin's logger.
89
95
  * @param compat - the loaded gate runtime (see `src/envinit.ts`).
@@ -1 +1 @@
1
- {"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../../src/provision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,UAAU,EACV,aAAa,EACb,eAAe,EAChB,MAAM,yBAAyB,CAAA;AAIhC,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA;AAE7C,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,cAAc,yBAAyB,CAAC,CAAA;AAEnE,mFAAmF;AACnF,eAAO,MAAM,cAAc,4BAA4B,CAAA;AAEvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EAMvD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAA6C,CAAA;AAE1F,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAmE,CAAA;AAKjH,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CACxC;AAED,0BAA0B;AAC1B,UAAU,SAAS;IACjB,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;CAChC;AAED,sDAAsD;AACtD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,2EAA2E;AAC3E,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMzE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAyBrE;AAoBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,aAAa,EACrB,IAAI,GAAE,UAAwB,GAC7B,SAAS,CAqBX;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,aAAa,GACpB,IAAI,CAaN;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAC/B,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAQ1C"}
1
+ {"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../../src/provision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,UAAU,EACV,aAAa,EACb,eAAe,EAChB,MAAM,yBAAyB,CAAA;AAIhC,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA;AAE7C,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,cAAc,yBAAyB,CAAC,CAAA;AAEnE,mFAAmF;AACnF,eAAO,MAAM,cAAc,4BAA4B,CAAA;AAEvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EAMvD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAA6C,CAAA;AAE1F,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAmE,CAAA;AAKjH,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CACxC;AAED,0BAA0B;AAC1B,UAAU,SAAS;IACjB,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;CAChC;AAED,sDAAsD;AACtD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,2EAA2E;AAC3E,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMzE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAyBrE;AAoBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,aAAa,EACrB,IAAI,GAAE,UAAwB,GAC7B,SAAS,CAMX;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,aAAa,GACpB,IAAI,CAaN;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAC/B,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAQ1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avantf/dsh-mem",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "avantf-mem DSH native Cordis plugin: host service + tools + RPC + client tabs 记忆/知识",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -41,7 +41,6 @@
41
41
  "node": ">=22.15.0 <23 || >=23.11.0"
42
42
  },
43
43
  "peerDependencies": {
44
- "@avantf/dsh-plugin-base": ">=0.3.0 <1.0.0",
45
44
  "@deepseek-ai/cordis": "^4.0.0",
46
45
  "@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.2 || ^0.1.7-rc.2 || ^0.2.0-rc.2",
47
46
  "@deepseek-ai/dsh-tools": "^0.1.5-rc.2 || ^0.1.7-rc.2 || ^0.2.0-rc.2",
@@ -67,6 +66,7 @@
67
66
  }
68
67
  },
69
68
  "dependencies": {
69
+ "@avantf/dsh-plugin-base": ">=0.3.0 <1.0.0",
70
70
  "exceljs": "^4.4.0",
71
71
  "unpdf": "^1.8.1",
72
72
  "yaml": "^2.5.0"
@@ -78,10 +78,10 @@
78
78
  },
79
79
  "devDependencies": {
80
80
  "@avantf/dsh-plugin-base": ">=0.3.0 <1.0.0",
81
- "@avantf/mem": "0.6.1",
82
- "@avantf/mem-contract": "0.6.1",
83
- "@avantf/mem-convert": "0.6.1",
84
- "@avantf/mem-provision": "0.6.1",
81
+ "@avantf/mem": "0.6.3",
82
+ "@avantf/mem-contract": "0.6.3",
83
+ "@avantf/mem-convert": "0.6.3",
84
+ "@avantf/mem-provision": "0.6.3",
85
85
  "@types/node": "^22.0.0",
86
86
  "@types/react": "^18.3.0",
87
87
  "lightningcss": "^1.32.0",