dsh-plugin-om 0.0.7 → 0.0.11

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
@@ -19,7 +19,12 @@
19
19
  dsh plugin --profile <profile> add dsh-plugin-om
20
20
  ```
21
21
 
22
- 需要重启dsh
22
+ > 对于 pnpm11+ 添加脚本失败的说明:
23
+ > 插件的依赖 `@huggingface/transformers` 自带原生构建脚本,pnpm 11 默认禁止依赖构建脚本,并会抛出异常。
24
+ > 把 `$DSH_HOME/profiles/<profile>/pnpm-workspace.yaml` 的 `allowBuilds` 改为 `true` 即可
25
+ > 或者使用`dsh plugin --profile <profile> add dsh-plugin-om --allow-build=onnxruntime-node --allow-build=protobufjs --allow-build=sharp`
26
+
27
+ 安装完成后,需要重启dsh
23
28
 
24
29
  可以通过`dsh --profile <profile> --dump-config`审查配置是否正确
25
30
 
@@ -36,7 +41,7 @@ dsh plugin --profile <profile> add dsh-plugin-om
36
41
 
37
42
  ### 开发插件
38
43
 
39
- 运行`pnpm dev`,等待`dist/index.mjs`构筑完毕(本地嵌入模型可在运行时自动下载;如需提前预下载用`pnpm run download:model`,已存在则跳过)
44
+ 运行`pnpm dev`,等待`dist/index.mjs`构筑完毕(本地嵌入模型可在运行时自动下载到跨版本共享目录;如需提前预下载用`pnpm run download:model`,已存在则跳过)
40
45
 
41
46
  在 `cordis.patch.yml` 里加入
42
47
 
@@ -82,7 +87,7 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
82
87
 
83
88
  - 复用dsh宿主提供的依赖,如 cordis / dsh-tools / zod 等
84
89
  - 例外:recall-semantic 的本地嵌入需要运行时依赖 `@huggingface/transformers`(transformers.js v4 + onnxruntime-node),模型小文件(config/tokenizer 等)随 npm 包分发(`models/`),onnx 二进制不随包分发(见下)
85
- - 模型二进制:量化 ONNX 约 113MB,超过 GitHub 单文件 100MB 限制,**不进入 git 仓库,也不随 npm 包分发**(`package.json` 的 `files` 以 `!models/*/onnx/*.onnx` 排除,见 `tests/package-pack.test.ts`)。改为**运行时按需下载**:仅当配置键 `semanticRecallEnabled` 启用且 `models/<id>/onnx/model_quantized.onnx` 缺失时,插件 apply 后台自动从 HuggingFace([Xenova 转换仓库](https://huggingface.co/Xenova/paraphrase-multilingual-MiniLM-L12-v2))下载到 `models/`(不阻塞;下载失败仅记日志,下次调用自动重试;未就绪时 `recall-semantic` 工具返回文案告知模型);本地开发也可用 `pnpm run download:model` 手动预下载(已存在则跳过,`--force` 强制重下);直连 `huggingface.co` 受限时设置环境变量 `HF_ENDPOINT=https://hf-mirror.com` 走镜像
90
+ - 模型二进制:量化 ONNX 约 113MB,超过 GitHub 单文件 100MB 限制,**不进入 git 仓库,也不随 npm 包分发**(`package.json` 的 `files` 以 `!models/*/onnx/*.onnx` 排除,见 `tests/package-pack.test.ts`)。改为**运行时按需下载**:仅当配置键 `semanticRecallEnabled` 启用且共享目录 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>/onnx/model_quantized.onnx` 缺失时,插件 apply 后台自动从 HuggingFace([Xenova 转换仓库](https://huggingface.co/Xenova/paraphrase-multilingual-MiniLM-L12-v2))下载到该共享目录(不阻塞;下载开始/结束经 console 与插件日志输出,失败仅记日志并附带设置 `HF_ENDPOINT=https://hf-mirror.com` 的镜像建议,下次调用自动重试;未就绪时 `recall-semantic` 工具返回文案告知模型);共享目录跨插件版本复用,升级/重装插件不重复下载,随包小文件(config/tokenizer 等)缺失时自动从插件包复制补齐(离线可用);本地开发也可用 `pnpm run download:model` 手动预下载到同一共享目录(已存在则跳过,`--force` 强制重下);直连 `huggingface.co` 受限时设置环境变量 `HF_ENDPOINT=https://hf-mirror.com` 走镜像
86
91
 
87
92
  ## 插件配置项
88
93
 
@@ -92,7 +97,7 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
92
97
  | `historyMergeRatio` | `0.2` | 反思阈值:摘要 ≥ 窗口 × 该比例触发精简合并 |
93
98
  | `compressMaxTokens` | `4096` | 单次摘要(观察/反思调用)生成上限 |
94
99
  | `tailMessageCount` | `10` | 尾部保留的不压缩消息条数(不压缩、不被替换、不进摘要日志) |
95
- | `modelDir` | 打包模型 | recall-semantic 嵌入模型目录(默认插件内 `models/` 目录,仅小文件随包分发;可指向自定义目录)。onnx 缺失且启用语义召回时运行时自动下载到该目录 |
100
+ | `modelDir` | 共享目录 | recall-semantic 嵌入模型目录(默认 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>`,跨插件版本共享;小文件随包分发并在缺失时自动补齐,onnx 缺失且启用语义召回时运行时自动下载到该目录;可指向自定义目录) |
96
101
  | `summaryMode` | `fork` | 摘要模式:`fork`(缺省)/ `new` / `disable`(关闭自动压缩);非法值在插件加载时报错(见[摘要模式](#摘要模式)) |
97
102
  | `debug` | dev | 压缩流程步骤级(debug)日志开关:`true` 强制开启、`false` 强制关闭;缺省按 `NODE_ENV !== 'production'` 判定(dev/test 输出,生产隐藏)。**失败日志不受此开关影响,始终输出** |
98
103
  | `recallEnabled` | `true` | 是否注册 `recall` 工具(`false` 时禁用,不注册) |
@@ -114,10 +119,10 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
114
119
  | `pnpm typecheck` | TypeScript 类型检查 |
115
120
  | `pnpm lint` / `pnpm format` | 代码检查 / 格式化 |
116
121
  | `pnpm test` | vitest 单元测试 |
117
- | `pnpm run download:model` | 手动预下载本地嵌入模型 ONNX(已存在跳过,`--force` 重下;运行时也会按需自动下载) |
122
+ | `pnpm run download:model` | 手动预下载本地嵌入模型 ONNX 到共享目录(默认 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>`;已存在跳过,`--force` 重下;运行时也会按需自动下载) |
118
123
  | `pnpm build` | |
119
124
  | `pnpm dev` | 自动打包 |
120
- | `pnpm run release` | CHANGELOG 归档 + 版本号更新 + 打 tag 推送 |
125
+ | `pnpm run release` | 先跑 check(typecheck+lint+test+build),再 CHANGELOG 归档 + 版本号更新 + 打 tag 推送 |
121
126
 
122
127
  ## 调用链和文件地图
123
128
 
@@ -143,16 +148,16 @@ src/
143
148
  ├── config.ts # 配置默认值 / 校验(缺省、null、空串回退默认值;数值键/布尔键/summaryMode/modelDir)
144
149
  ├── utils.ts # 零依赖工具函数(配置校验 / 文本渲染 / 主会话判定 / 路由解析)
145
150
  ├── log-index.ts # 完整消息索引(index 定位 user/assistant/toolcall 三类完整消息;recall 与摘要共用)
146
- ├── embedding.ts # 本地 ONNX 嵌入(@huggingface/transformers + 本地模型;运行时按需下载编排 / 懒加载 / 批量 / cosine)
147
- ├── model-download.ts # 模型下载原语(modelSourceUrl / needsDownload / 原子落盘;运行时与 dev CLI 共用)
151
+ ├── embedding.ts # 本地 ONNX 嵌入(@huggingface/transformers + 本地模型;共享目录解析 / 小文件补齐 / 运行时按需下载编排 / 懒加载 / 批量 / cosine)
152
+ ├── model-download.ts # 模型下载原语(modelSourceUrl / needsDownload / 原子落盘 / 开始结束日志与失败镜像建议;运行时与 dev CLI 共用)
148
153
  ├── summarize.ts # 观察/反思 persona + 提示词 + 直连 ctx.llm.stream() 摘要(fork/new 双模式;extractSummaryLog 提取校验;流式 usage 归入主会话)
149
154
  ├── recall.ts # recall 工具
150
155
  ├── semantic-recall.ts # recall-semantic 工具(query 语义检索 + 区间限定 + 回退全量 + 匹配说明)
151
- └── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / 中断扫描 / source 标记判定摘要消息 / compaction/* 生命周期事件 + checkpoint 替换)
156
+ └── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / source 标记判定摘要消息 / compaction/* 生命周期事件 + checkpoint 替换)
152
157
  models/
153
- └── paraphrase-multilingual-MiniLM-L12-v2/ # 嵌入模型目录(小文件随包分发;onnx 二进制不随包分发、由运行时按需下载到此处,不进 git)
158
+ └── paraphrase-multilingual-MiniLM-L12-v2/ # 嵌入模型目录(小文件随包分发;onnx 二进制不随包分发、由运行时按需下载到跨版本共享目录,不进 git)
154
159
  scripts/ # release-archive.mjs(CHANGELOG 归档)/ download-model.mjs(开发手动预下载 CLI)
155
- tests/ # vitest 单元测试(140 例)
160
+ tests/ # vitest 单元测试(143 例)
156
161
  .dsh/skills/ # 项目级 skill(feature-defect-workflow:需求/缺陷完成工作流)
157
162
  ```
158
163
 
@@ -27,19 +27,12 @@ export declare function isPairBalancedAfter(session: Session, seq: number): bool
27
27
  * 观察压缩区间:pre-step 触发时日志 call-result 完备,区间不再受 turn/end 封顶——
28
28
  * 头部 → 表层长度-1-tailCount(尾部保留 tailCount 条不压缩),当前 turn 中已完备的
29
29
  * 消息同样可压缩;区间终点回退到 tool-call/result 配对平衡点(不切段)。
30
- * lastEndSeq 仅为中断扫描提供最后一个已结束 turn 的边界(无则 -1)。
31
30
  */
32
31
  export declare function computeCompressRange(session: Session, tailCount: number): {
33
32
  start: number;
34
33
  end: number;
35
34
  shadowedSeqs: number[];
36
- lastEndSeq: number;
37
35
  } | undefined;
38
- /**
39
- * 中断标记行:范围内 turn/end 以 aborted(含 cause 类型)或 interrupted 结束的轮次
40
- * (标记用途:让摘要 AI 理解中断原因)。
41
- */
42
- export declare function scanInterruptions(session: Session, fromSeq: number, toSeq: number): string[];
43
36
  /**
44
37
  * 提取遮蔽区间内最后一次 <om-history> 压缩日志(内文 + seq)。
45
38
  * 按表层顺序(shadowedSeqs)扫描:单节点替换(反思)后摘要节点 seq 可能大于
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EACV,KAAK,EAEL,OAAO,EACP,YAAY,EACZ,OAAO,EAIR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,KAAK,YAAY,EAAsB,MAAM,YAAY,CAAC;AAEjF,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAsBD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AAE3E;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAYzF;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS7F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAc1E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAyBxF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB5F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS3C;AAoGD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Ff;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAwIf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAwCf"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EACV,KAAK,EAEL,OAAO,EACP,YAAY,EACZ,OAAO,EAIR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,KAAK,YAAY,EAAsB,MAAM,YAAY,CAAC;AAEjF,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAsBD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AAE3E;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAYzF;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS7F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAc1E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAuBpE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS3C;AAoGD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Ff;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Hf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAwCf"}
package/dist/config.d.ts CHANGED
@@ -24,7 +24,7 @@ export type PluginConfig = {
24
24
  recallEnabled: boolean;
25
25
  /** 是否注册 recall-semantic 工具(缺省 true;false 时不注册、不触发模型下载)。 */
26
26
  semanticRecallEnabled: boolean;
27
- /** 语义召回嵌入模型目录(默认插件打包的本地模型;可指向自定义模型目录)。 */
27
+ /** 语义召回嵌入模型目录(默认跨版本共享目录;可指向自定义模型目录)。 */
28
28
  modelDir: string;
29
29
  };
30
30
  /** 默认配置(冻结对象,resolveConfig 合并的基底;debug 缺省值在解析时按 NODE_ENV 判定)。 */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAUhD,CAAC;AAkCH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAK5D;AAUD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CAsCnE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAUhD,CAAC;AAkCH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAK5D;AAUD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CAsCnE"}
@@ -2,7 +2,9 @@
2
2
  * 本地语义嵌入(recall-semantic 的向量引擎)。
3
3
  *
4
4
  * - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX,多语言,
5
- * 中英 + 代码均可处理),模型文件在 models/ 下,完全离线、零下载。
5
+ * 中英 + 代码均可处理)。模型目录默认 = $DSH_HOME/plugin-data/dsh-plugin-om/
6
+ * models/<模型id>/(跨插件版本共享):随包小文件(config/tokenizer 等)缺失时
7
+ * 从打包目录复制补齐(离线可用),onnx 二进制按需下载。
6
8
  * - 运行时下载:模型 onnx 缺失时由 ensureModelReady 后台下载(不阻塞、单飞、
7
9
  * 失败自动重试),下载逻辑见 model-download.ts;就绪前工具告知模型。
8
10
  * - 懒加载:首次调用才 `import('@huggingface/transformers')` 并加载 pipeline,
@@ -17,6 +19,20 @@ import { EMBEDDING_MODEL_ID, type ModelFetch } from './model-download.ts';
17
19
  /** 打包模型目录:<包根>/models/<model-id>/。 */
18
20
  export declare const BUNDLED_MODEL_DIR: string;
19
21
  export { EMBEDDING_MODEL_ID };
22
+ /** DSH 用户数据根目录:$DSH_HOME 优先(空白视为未设置),缺省 ~/.dsh(与宿主解析规则一致)。 */
23
+ export declare function resolveDshHome(): string;
24
+ /**
25
+ * 跨插件版本共享的默认模型目录:$DSH_HOME/plugin-data/dsh-plugin-om/models/<模型id>。
26
+ * 不随插件包(版本)变化,升级/重装插件后仍复用同一份已下载模型(onnx 不重复下载)。
27
+ */
28
+ export declare function sharedModelDir(): string;
29
+ /**
30
+ * 补齐模型目录的随包小文件(config/tokenizer 等):modelDir 与打包目录不同
31
+ * (如共享目录)时,把打包目录中缺失的小文件复制过去,保证模型可离线加载。
32
+ * 已存在的文件不覆盖(同一模型文件跨版本稳定);打包目录本身缺失则跳过。
33
+ * 幂等、无网络。onnx 二进制不在此列(按需下载,见 model-download.ts)。
34
+ */
35
+ export declare function ensureModelSmallFiles(modelDir: string, bundledDir?: string): void;
20
36
  /** 嵌入函数类型:批量文本 → 每条一个向量(Float32Array)。 */
21
37
  export type EmbedFn = (texts: readonly string[]) => Promise<Float32Array[]>;
22
38
  /**
@@ -34,9 +50,10 @@ export type ModelStatus = 'ready' | 'downloading';
34
50
  * - 缺失 → 启动后台下载(不阻塞,单飞)并返回 'downloading';下载失败仅调用
35
51
  * warn 记录日志并结束本次尝试,下次调用会重新触发下载(自动重试)。
36
52
  * - warn 可选:下载失败时的日志回调(默认静默;apply 注入 ctx.logger.warn)。
53
+ * - log 可选:下载开始/结束日志回调(默认静默;apply 注入 console.log + ctx.logger.info 双通道)。
37
54
  * - fetchImpl 可注入(测试传替身,默认全局 fetch)。
38
55
  */
39
- export declare function ensureModelReady(modelDir?: string, warn?: (message: string) => void, fetchImpl?: ModelFetch): Promise<ModelStatus>;
56
+ export declare function ensureModelReady(modelDir?: string, warn?: (message: string) => void, fetchImpl?: ModelFetch, log?: (message: string) => void): Promise<ModelStatus>;
40
57
  /** 重置下载状态(测试用:清空在途下载任务记录)。 */
41
58
  export declare function resetModelDownloads(): void;
42
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../src/embedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAEL,kBAAkB,EAClB,KAAK,UAAU,EAGhB,MAAM,qBAAqB,CAAC;AAK7B,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,QAAsD,CAAC;AAErF,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,0CAA0C;AAC1C,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;AAQ5E;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,GAAE,MAA0B,GAAG,OAAO,CAAC,OAAO,CAAC,CAkClF;AAED,mCAAmC;AACnC,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,aAAa,CAAC;AAKlD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,GAAE,MAA0B,EACpC,IAAI,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,EAC1C,SAAS,CAAC,EAAE,UAAU,GACrB,OAAO,CAAC,WAAW,CAAC,CAmBtB;AAED,8BAA8B;AAC9B,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,EACnC,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,GAClC,MAAM,CAkBR"}
1
+ {"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../src/embedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,EAEL,kBAAkB,EAElB,KAAK,UAAU,EAGhB,MAAM,qBAAqB,CAAC;AAK7B,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,QAAsD,CAAC;AAErF,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,8DAA8D;AAC9D,wBAAgB,cAAc,IAAI,MAAM,CAIvC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAA0B,GACrC,IAAI,CASN;AAED,0CAA0C;AAC1C,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;AAQ5E;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,GAAE,MAA0B,GAAG,OAAO,CAAC,OAAO,CAAC,CAoClF;AAED,mCAAmC;AACnC,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,aAAa,CAAC;AAKlD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,GAAE,MAA0B,EACpC,IAAI,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,EAC1C,SAAS,CAAC,EAAE,UAAU,EACtB,GAAG,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GACxC,OAAO,CAAC,WAAW,CAAC,CAqBtB;AAED,8BAA8B;AAC9B,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,EACnC,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,GAClC,MAAM,CAkBR"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,6BAA6B;AAC7B,eAAO,MAAM,IAAI,kBAAkB,CAAC;AAEpC,wDAAwD;AACxD,eAAO,MAAM,MAAM,UAA6C,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAkD1D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,6BAA6B;AAC7B,eAAO,MAAM,IAAI,kBAAkB,CAAC;AAEpC,wDAAwD;AACxD,eAAO,MAAM,MAAM,UAA6C,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAwD1D"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
+ import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
1
3
  import path from "node:path";
2
4
  import { fileURLToPath } from "node:url";
3
- import { mkdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
5
  import { z } from "zod";
5
6
  //#region src/constants.ts
6
7
  /**
@@ -254,20 +255,18 @@ function makeLogger(ctx, debug) {
254
255
  //#endregion
255
256
  //#region src/summarize.ts
256
257
  /** 观察者 persona:只针对未压缩消息产出观察日志,不用工具、不展示思考。 */
257
- const OBSERVER_PERSONA = "你是 dsh-plugin-om 的上下文观察者(Observer,机制参考 Mastra Observational Memory):把会话中尚未压缩的消息压缩为一份观察日志。不用工具、不展示思考、不评价代码、不输出多余文字。";
258
+ const OBSERVER_PERSONA = "把会话中尚未压缩的消息压缩为一份观察日志。不用工具、不展示思考、不评价代码、不输出多余文字。";
258
259
  /** 反思者 persona:只精简合并当前摘要,不用工具、不展示思考。 */
259
- const REFLECTOR_PERSONA = "你是 dsh-plugin-om 的上下文反思者(Reflector,机制参考 Mastra Observational Memory):把当前 <om-history> 压缩日志精简合并为一份更紧凑的日志。不用工具、不展示思考、不输出多余文字。";
260
+ const REFLECTOR_PERSONA = "把当前 <om-history> 压缩日志精简合并为一份更紧凑的日志。不用工具、不展示思考、不输出多余文字。";
260
261
  /**
261
262
  * 构建观察指令主体:任务声明(fork:停止任务/禁止工具;new:说明总结日志)+ 模式相关
262
263
  * 的上下文定位与压缩范围(fork:上方完整会话记录——尾部已在输入中实际截断,提示词不含
263
264
  * 尾部规则;new:下方消息即压缩对象)+ 完整消息与 index 定义(三类合并规则、起始编号)+
264
- * 规则(用户消息完整保留原文 / AI 消息模块化压缩——工具调用按目的聚合——不限于 run_code /
265
- * 倾向于新消息 / 中断标注 / 未完成写进度与下一步)+ 输出格式(合法 XML)+ 中断标记 +
266
- * 追加说明。persona 由调用方拼接到指令开头。
265
+ * 规则(用户消息完整保留原文 / AI 消息按模块压缩 / 本指令不入日志 / index/start/end 连续 /
266
+ * 倾向于新消息 / 未完成写进度与下一步)+ 输出格式(合法 XML)+ 追加说明。
267
+ * persona 由调用方拼接到指令开头。
267
268
  */
268
269
  function buildObservePrompt(options) {
269
- /** 中断标记段落(无则标注「无」)。 */
270
- const interruptionSection = options.interruptions.length > 0 ? options.interruptions : ["(无)"];
271
270
  return [
272
271
  options.mode === "fork" ? "停止一切现有任务,禁止调用任何工具,改为将过往消息总结为一份日志。" : "将过往消息总结为一份日志。",
273
272
  "",
@@ -278,32 +277,31 @@ function buildObservePrompt(options) {
278
277
  ] : ["下方的消息记录是本次要压缩的全部消息(上一个 <om-history> 块之后的新消息;不含旧压缩日志、不含尾部)。", "你的压缩结果会作为新的 <om-history> 块追加到已有压缩日志之后。"],
279
278
  "",
280
279
  "【完整消息与 index】",
281
- "- 完整消息是压缩日志与 recall 共用的定位单位,每一条占一个 index(从 0 起、按会话顺序递增、全局稳定)。完整消息分三类:用户消息占一条;AI 文本占一条;工具调用及其结果占一条(每个 tool-call 与其 result 各一条,同一条 AI 消息里的文本与工具调用拆开)。",
280
+ "- 完整消息分三类:用户消息占一条;AI 文本占一条;工具调用及其结果占一条(每个 tool-call 与其 result 各一条,同一条 AI 消息里的文本与工具调用拆开)。每条完整消息有一个 index(从 0 起、按会话顺序递增、全局稳定),index 是该条完整消息的序号。",
282
281
  `- 本次要压缩的新消息从 index ${options.startIndex} 开始编号,按日志顺序逐条递增;旧 <${HISTORY_TAG}> 中已有的 index 无需理会,也不要改动旧条目。`,
283
282
  "",
284
283
  "【规则】",
284
+ "- 当前消息仅作为指令,**不得**进入日志。",
285
285
  "- 用户消息完整保留原文,输出为 <user_message index=\"N\"> 条目(N 为该条完整消息的 index,内容为消息原文,不概括、不省略)。",
286
- "- AI 消息划分模块压缩(按模块聚合 assistant 的方式):所有工具调用(run_code 与其他工具同等对待,不限于 run_code)按调用目的聚合为一行 toolcall index:<该条完整消息的 index> purpose:<聚合目的> summary:<行为与结果摘要>;相邻的 AI 侧完整消息(文本与工具调用)聚合为一个 <assistant> 模块,start/end 标注模块覆盖的 index 区间(start 为首条 index、end 为末条 index);重要工具调用(关键决策、写文件等)单独用 <assistant index=\"N\"> 呈现。工具组内部细节(参数、完整输出)不保留,需要原文时用 recall 按 index 回看(start/end 定位)。",
287
- "- 条目须按 index 顺序覆盖本次压缩的全部完整消息,不遗漏。",
286
+ "- AI 消息按照内在关联,划分为**连续消息**组成的模块,消息模块的划分方式不受限制,但内部须有足够的**逻辑连贯性**,应当是AI基于某个目的做出了一系列动作或论述",
287
+ "- 模块内的消息聚合输出为<assistant start=\"\" end=\"\">块,start/end表示当前模块首尾消息的index;块内不描述单条消息的细节,只描述当前模块的**目的、行为、结果**;最后一个<assistant>块,额外包含`下一步计划`",
288
+ "- 对于模块涉及到的具体文件,应当出现在<assistant>块中,多个前缀相同的路径要合并简写",
289
+ "- 对于单条重要的AI完整消息,以<assistant index=\"\">单独呈现,其内容不受限制",
290
+ "- 条目按 index 顺序覆盖本次压缩的全部完整消息,index/start/end 必须连续(区间内 index 连续、相邻条目相接),不跳号、不重叠、不遗漏。",
288
291
  "- 总结时倾向于新消息,旧消息一句话带过即可;新旧消息冲突时强调新消息,不修改旧日志条目。",
289
- "- 若【中断标记】非空,在对应位置明确写出中断(例如「被用户打断,因此上一段工作未完成」),帮助后续理解用户为何再次输入消息、为何不延续之前的工作。",
290
- "- 若当前工作看起来未完成(最后一次工具调用没有结果、或对话被中断/异常结束),在日志末尾说明当前进度与下一步要做什么。",
291
292
  "【输出格式】只输出一个 <om-history> 包裹的合法 XML 日志块,不要解释、不要复述规则:",
292
293
  `<${HISTORY_TAG}>`,
293
294
  "<user_message index=\"(index)\">",
294
295
  "(user 消息原文)",
295
296
  "</user_message>",
296
297
  "<assistant start=\"(起始 index)\" end=\"(结束 index)\">",
297
- "(压缩模块:toolcall 聚合行等,按模块聚合 assistant 的方式)",
298
+ "(模块的目的、行为与结果摘要)",
298
299
  "</assistant>",
299
300
  "<assistant index=\"(index)\">",
300
- "(重要调用单独条目)",
301
+ "(单条完整消息的模块摘要)",
301
302
  "</assistant>",
302
303
  `</${HISTORY_TAG}>`,
303
304
  "",
304
- "【中断标记】",
305
- ...interruptionSection,
306
- "",
307
305
  ...options.hasOldHistory ? [`【说明】你的压缩结果会被直接追加到上一次压缩产物(<${HISTORY_TAG}>)的末尾,作为新的 <${HISTORY_TAG}> 块;条目格式与本块一致。`] : [`【说明】你的压缩结果将成为第一条 <${HISTORY_TAG}> 压缩日志。`]
308
306
  ].join("\n");
309
307
  }
@@ -316,11 +314,14 @@ function buildReflectPrompt(mode) {
316
314
  ...mode === "new" ? ["下方的消息记录包含当前的 <om-history> 压缩日志(最后一次 <om-history> 块)。", "只对这份压缩日志做精简合并;不要涉及日志之外的消息。"] : ["上方的消息记录是主会话的完整历史,其中包含当前的 <om-history> 压缩日志(最后一次 <om-history> 块)。", "只对这份压缩日志做精简合并;不要涉及日志之外的消息。"],
317
315
  "",
318
316
  "【规则】",
319
- "- 完整消息是压缩日志的定位单位(用户消息 / AI 文本 / 工具调用及其结果各占一个 index,从 0 起、全局稳定);合并时单个完整消息的 index 不重新编号,被删除的条目 index 空缺。",
320
- "- 用户消息保留要点与 index(格式:<user_message index=\"(index)\"> 要点 </user_message>);可省略的条目删除。",
321
- "- toolcall 条目按调用目的进一步聚合,保留 index;不重要的条目 summary 写「(略)」。",
322
- "- 相邻的 assistant 条目可合并为一个 <assistant> 模块,start/end 随合并后的区间变化。",
323
- "- 保留中断说明与未完成说明(若原日志中有)。",
317
+ "- 当前消息仅作为指令,**不得**进入日志。",
318
+ "- 完整消息分三类(用户消息 / AI 文本 / 单个工具调用及其结果),各占一个 index(从 0 起、会话内全局稳定),index 是该条完整消息的序号。",
319
+ "- 将AI 消息按照内在关联,划分为**连续消息**组成的模块,消息模块的划分方式不受限制,但内部须有足够的**逻辑连贯性**,应当是AI基于某个目的做出了一系列动作或论述",
320
+ "- 模块内的消息聚合输出为<assistant start=\"\" end=\"\">块,start/end表示当前模块首尾消息的index;块内不描述单条消息的细节,只描述当前模块的**目的、行为、结果**;最后一个<assistant>块,额外包含`下一步计划`",
321
+ "- 对于模块涉及到的具体文件,应当出现在<assistant>块中,多个前缀相同的路径要合并简写",
322
+ "- 对于单条重要的AI完整消息,以<assistant index=\"\">单独呈现,其内容不受限制",
323
+ "- 用户消息保留要点与 index(格式:<user_message index=\"(index)\"> 要点 </user_message>);不重要的内容概括为「(略)」。",
324
+ "- 条目按 index 顺序覆盖日志中的全部完整消息,index/start/end 必须连续(区间内 index 连续、相邻条目相接),不跳号、不重叠、不遗漏;合并时条目的 index 不重新编号。",
324
325
  "- 过时事实丢弃,不逐字复制旧文本;新旧条目冲突时保留新条目。",
325
326
  "【输出格式】只输出一个 <om-history> 包裹的合法 XML 日志块,不要解释、不要复述规则:",
326
327
  `<${HISTORY_TAG}>`,
@@ -328,10 +329,10 @@ function buildReflectPrompt(mode) {
328
329
  "(user 消息要点)",
329
330
  "</user_message>",
330
331
  "<assistant start=\"(起始 index)\" end=\"(结束 index)\">",
331
- "(压缩模块:toolcall 聚合行等,按模块聚合 assistant 的方式)",
332
+ "(模块的目的、行为与结果摘要)",
332
333
  "</assistant>",
333
334
  "<assistant index=\"(index)\">",
334
- "(重要调用单独条目)",
335
+ "(单条完整消息的模块摘要)",
335
336
  "</assistant>",
336
337
  `</${HISTORY_TAG}>`,
337
338
  "",
@@ -442,7 +443,7 @@ function buildSummaryOptions(session, instruction, contextText, maxTokens, mode,
442
443
  };
443
444
  }
444
445
  /** 产出日志后插入首个 <om-history> 后的格式说明(XML 注释,避免被误读为日志条目)。 */
445
- const HISTORY_FORMAT_NOTE = "<!-- 本日志以「完整消息」为定位单位:完整消息分三类(用户消息 / AI 文本 / 单个工具调用及其结果),各占一个 index(从 0 起、会话内全局稳定);<user_message index=\"N\"> 与 <assistant index=\"N\"> 定位单条完整消息,<assistant start=\"A\" end=\"B\"> 定位连续区间(含两端)。需要原文时用 recall(start, end) 按 index 回看。 -->";
446
+ const HISTORY_FORMAT_NOTE = "<!-- 完整消息分三类(用户消息 / AI 文本 / 单个工具调用及其结果),各占一个 index(从 0 起、会话内全局稳定);<user_message index=\"N\"> 与 <assistant index=\"N\"> 表示单条完整消息,<assistant start=\"A\" end=\"B\"> 表示多条完整消息,start/end是首尾消息的index -->";
446
447
  /**
447
448
  * 从 AI 摘要输出中提取合法日志(不信任 AI 的总结结果):
448
449
  * - 取首个 <om-history> 到最后一个 </om-history>(含两个首尾)切为日志;
@@ -617,7 +618,6 @@ function isPairBalancedAfter(session, seq) {
617
618
  * 观察压缩区间:pre-step 触发时日志 call-result 完备,区间不再受 turn/end 封顶——
618
619
  * 头部 → 表层长度-1-tailCount(尾部保留 tailCount 条不压缩),当前 turn 中已完备的
619
620
  * 消息同样可压缩;区间终点回退到 tool-call/result 配对平衡点(不切段)。
620
- * lastEndSeq 仅为中断扫描提供最后一个已结束 turn 的边界(无则 -1)。
621
621
  */
622
622
  function computeCompressRange(session, tailCount) {
623
623
  /** 当前表层节点(按日志顺序)。 */
@@ -642,33 +642,10 @@ function computeCompressRange(session, tailCount) {
642
642
  return {
643
643
  start,
644
644
  end,
645
- shadowedSeqs: surface.slice(0, endIdx + 1),
646
- lastEndSeq: session.events.findLast((event) => event.type === "turn/end")?.seq ?? -1
645
+ shadowedSeqs: surface.slice(0, endIdx + 1)
647
646
  };
648
647
  }
649
648
  /**
650
- * 中断标记行:范围内 turn/end 以 aborted(含 cause 类型)或 interrupted 结束的轮次
651
- * (标记用途:让摘要 AI 理解中断原因)。
652
- */
653
- function scanInterruptions(session, fromSeq, toSeq) {
654
- /** 标记行缓冲区。 */
655
- const marks = [];
656
- for (let seq = fromSeq + 1; seq <= toSeq; seq += 1) {
657
- /** 当前待检查事件。 */
658
- const event = session.events[seq];
659
- if (event?.type !== "turn/end") continue;
660
- /** 结束原因(判别 kind)。 */
661
- const reason = event.data.reason;
662
- if (!reason || typeof reason !== "object") continue;
663
- if (reason.kind === "aborted") {
664
- /** 取消来源(user/parent/hook/disposed;未知标记 unknown)。 */
665
- const cause = reason.reason?.kind ?? "unknown";
666
- marks.push(`[interrupted] turn ${String(event.data.turn)} 被中断(aborted,原因 ${cause})`);
667
- } else if (reason.kind === "interrupted") marks.push(`[interrupted] turn ${String(event.data.turn)} 因崩溃恢复中断(interrupted)`);
668
- }
669
- return marks;
670
- }
671
- /**
672
649
  * 提取遮蔽区间内最后一次 <om-history> 压缩日志(内文 + seq)。
673
650
  * 按表层顺序(shadowedSeqs)扫描:单节点替换(反思)后摘要节点 seq 可能大于
674
651
  * 被压缩消息的 seq,按 seq 区间扫描会漏(start > end)。
@@ -863,18 +840,15 @@ async function observePass(ctx, agent, config, window, tailCount, target, signal
863
840
  logger.step(`观察:压缩区间 [${range.start}..${range.end}],遮蔽 ${range.shadowedSeqs.length} 个表层节点`);
864
841
  /** 区间内旧摘要(追加基准;无则首次压缩)。 */
865
842
  const history = extractHistoryText(session, range.shadowedSeqs);
866
- /** 中断标记行。 */
867
- const interruptions = scanInterruptions(session, Math.min(...range.shadowedSeqs), range.lastEndSeq);
868
843
  const actualTailCount = [...session.surface.nodes].length - range.shadowedSeqs.length;
869
844
  /** 遮蔽 seq 集合(计算新消息起始 index)。 */
870
845
  const shadowedSet = new Set(range.shadowedSeqs);
871
846
  /** 压缩区间内第一个完整消息的 index(新消息起始编号;区间内无完整消息则 0)。 */
872
847
  const startIndex = indexCompleteMessages(session).find((cm) => cm.seqs.every((seq) => shadowedSet.has(seq)))?.index ?? 0;
873
- logger.step(`观察:实际保留尾部 ${actualTailCount} 条(不压缩、不进日志),中断标记 ${interruptions.length} 条,新消息起始 index ${startIndex}`);
848
+ logger.step(`观察:实际保留尾部 ${actualTailCount} 条(不压缩、不进日志),新消息起始 index ${startIndex}`);
874
849
  /** 观察指令(persona + 规则主体)。 */
875
850
  const prompt = buildObservePrompt({
876
851
  startIndex,
877
- interruptions,
878
852
  hasOldHistory: history !== void 0,
879
853
  mode: config.summaryMode
880
854
  });
@@ -989,10 +963,13 @@ async function maybeCompress(ctx, agent, config, signal) {
989
963
  *
990
964
  * - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX ~113MB,超过
991
965
  * GitHub 单文件 100MB 限制,不能进入 git 仓库;仅小文件随仓库提交)。
992
- * - 下载目标:<modelDir>/onnx/model_quantized.onnx(modelDir 默认 = 插件包内
993
- * models/<id>/,与 embedding.ts 的本地加载路径一致;自定义 modelDir 同样生效)。
966
+ * - 下载目标:<modelDir>/onnx/model_quantized.onnx(modelDir 默认 = 跨版本共享目录
967
+ * $DSH_HOME/plugin-data/dsh-plugin-om/models/<id>/,见 embedding.ts sharedModelDir;
968
+ * 自定义 modelDir 同样生效)。
994
969
  * - 直连 huggingface.co 受限时设置环境变量 HF_ENDPOINT=https://hf-mirror.com 走镜像。
995
970
  * - 原子落盘:先写 .tmp 再改名,避免半截文件被当成完整模型加载。
971
+ * - 日志:下载开始/结束(含跳过)经 log 回调输出(默认 console.log);失败时
972
+ * 错误消息附带 HF_ENDPOINT=https://hf-mirror.com 镜像建议。
996
973
  *
997
974
  * 运行时编排见 embedding.ts 的 ensureModelReady(下载单飞、未就绪不阻塞);
998
975
  * 本模块只提供纯下载原语(供 src 与 scripts/download-model.mjs CLI 复用)。
@@ -1001,6 +978,13 @@ async function maybeCompress(ctx, agent, config, signal) {
1001
978
  const EMBEDDING_MODEL_ID = "paraphrase-multilingual-MiniLM-L12-v2";
1002
979
  /** transformers.js 转换仓库(Xenova org)中量化 ONNX 文件的相对路径(相对模型目录)。 */
1003
980
  const ONNX_REL = "onnx/model_quantized.onnx";
981
+ /** 随 npm 包分发的小模型文件(相对模型目录;onnx 二进制不随包分发,见 tests/package-pack.test.ts)。 */
982
+ const MODEL_SMALL_FILES = [
983
+ "config.json",
984
+ "special_tokens_map.json",
985
+ "tokenizer.json",
986
+ "tokenizer_config.json"
987
+ ];
1004
988
  /**
1005
989
  * HuggingFace 原始文件 URL(resolve 会 302 到 CDN,fetch 自动跟随重定向)。
1006
990
  * base 默认 huggingface.co,可用环境变量 HF_ENDPOINT 覆盖(直连受限时改用镜像,如 https://hf-mirror.com)。
@@ -1027,12 +1011,14 @@ function needsDownload(target, { force = false } = {}) {
1027
1011
  }
1028
1012
  /**
1029
1013
  * 下载模型到 <modelDir>/onnx/model_quantized.onnx。
1030
- * 可注入 fetchImpl / log(供测试);失败时清理临时文件并抛出。
1014
+ * 开始/结束(含跳过)经 log 回调输出(默认 console.log);失败时清理临时文件,
1015
+ * 并在错误消息中附带镜像建议(直连受限时设置 HF_ENDPOINT=https://hf-mirror.com)。
1016
+ * 可注入 fetchImpl / log(供测试)。
1031
1017
  */
1032
1018
  async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log = console.log } = {}) {
1033
1019
  const target = modelTargetPath(modelDir);
1034
1020
  if (!needsDownload(target, { force })) {
1035
- log(`[download-model] 已存在,跳过:${target}`);
1021
+ log(`[download-model] 模型已存在,跳过下载:${target}`);
1036
1022
  return {
1037
1023
  downloaded: false,
1038
1024
  bytes: 0,
@@ -1042,13 +1028,14 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
1042
1028
  const url = modelSourceUrl();
1043
1029
  mkdirSync(path.dirname(target), { recursive: true });
1044
1030
  const tmp = `${target}.tmp`;
1031
+ log(`[download-model] 开始下载:${url}`);
1045
1032
  try {
1046
1033
  const resp = await fetchImpl(url);
1047
1034
  if (!resp.ok) throw new Error(`下载失败:HTTP ${resp.status} ${resp.statusText}(${url})`);
1048
1035
  const buf = Buffer.from(await resp.arrayBuffer());
1049
1036
  writeFileSync(tmp, buf);
1050
1037
  renameSync(tmp, target);
1051
- log(`[download-model] 已下载 ${buf.length} 字节 → ${target}`);
1038
+ log(`[download-model] 下载完成:${buf.length} 字节 → ${target}`);
1052
1039
  return {
1053
1040
  downloaded: true,
1054
1041
  bytes: buf.length,
@@ -1056,7 +1043,8 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
1056
1043
  };
1057
1044
  } catch (err) {
1058
1045
  rmSync(tmp, { force: true });
1059
- throw err;
1046
+ const detail = err instanceof Error ? err.message : String(err);
1047
+ throw new Error(`${detail};如直连 HuggingFace 受限,请设置环境变量 HF_ENDPOINT=https://hf-mirror.com 后重试`);
1060
1048
  }
1061
1049
  }
1062
1050
  //#endregion
@@ -1065,7 +1053,9 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
1065
1053
  * 本地语义嵌入(recall-semantic 的向量引擎)。
1066
1054
  *
1067
1055
  * - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX,多语言,
1068
- * 中英 + 代码均可处理),模型文件在 models/ 下,完全离线、零下载。
1056
+ * 中英 + 代码均可处理)。模型目录默认 = $DSH_HOME/plugin-data/dsh-plugin-om/
1057
+ * models/<模型id>/(跨插件版本共享):随包小文件(config/tokenizer 等)缺失时
1058
+ * 从打包目录复制补齐(离线可用),onnx 二进制按需下载。
1069
1059
  * - 运行时下载:模型 onnx 缺失时由 ensureModelReady 后台下载(不阻塞、单飞、
1070
1060
  * 失败自动重试),下载逻辑见 model-download.ts;就绪前工具告知模型。
1071
1061
  * - 懒加载:首次调用才 `import('@huggingface/transformers')` 并加载 pipeline,
@@ -1080,6 +1070,35 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
1080
1070
  const here = path.dirname(fileURLToPath(import.meta.url));
1081
1071
  /** 打包模型目录:<包根>/models/<model-id>/。 */
1082
1072
  const BUNDLED_MODEL_DIR = path.join(here, "..", "models", EMBEDDING_MODEL_ID);
1073
+ /** DSH 用户数据根目录:$DSH_HOME 优先(空白视为未设置),缺省 ~/.dsh(与宿主解析规则一致)。 */
1074
+ function resolveDshHome() {
1075
+ const fromEnv = process.env.DSH_HOME;
1076
+ if (fromEnv !== void 0 && fromEnv.trim().length > 0) return path.resolve(fromEnv);
1077
+ return path.join(homedir(), ".dsh");
1078
+ }
1079
+ /**
1080
+ * 跨插件版本共享的默认模型目录:$DSH_HOME/plugin-data/dsh-plugin-om/models/<模型id>。
1081
+ * 不随插件包(版本)变化,升级/重装插件后仍复用同一份已下载模型(onnx 不重复下载)。
1082
+ */
1083
+ function sharedModelDir() {
1084
+ return path.join(resolveDshHome(), "plugin-data", "dsh-plugin-om", "models", EMBEDDING_MODEL_ID);
1085
+ }
1086
+ /**
1087
+ * 补齐模型目录的随包小文件(config/tokenizer 等):modelDir 与打包目录不同
1088
+ * (如共享目录)时,把打包目录中缺失的小文件复制过去,保证模型可离线加载。
1089
+ * 已存在的文件不覆盖(同一模型文件跨版本稳定);打包目录本身缺失则跳过。
1090
+ * 幂等、无网络。onnx 二进制不在此列(按需下载,见 model-download.ts)。
1091
+ */
1092
+ function ensureModelSmallFiles(modelDir, bundledDir = BUNDLED_MODEL_DIR) {
1093
+ if (path.resolve(modelDir) === path.resolve(bundledDir)) return;
1094
+ for (const rel of MODEL_SMALL_FILES) {
1095
+ const src = path.join(bundledDir, rel);
1096
+ const dest = path.join(modelDir, rel);
1097
+ if (existsSync(dest) || !existsSync(src)) continue;
1098
+ mkdirSync(path.dirname(dest), { recursive: true });
1099
+ copyFileSync(src, dest);
1100
+ }
1101
+ }
1083
1102
  /** 单批最大文本数(避免单次推理过大)。 */
1084
1103
  const BATCH_SIZE = 32;
1085
1104
  /** 懒加载中的 pipeline 单例(null = 尚未加载/已重置)。 */
@@ -1091,6 +1110,7 @@ let pipelinePromise = null;
1091
1110
  function getEmbedder(modelDir = BUNDLED_MODEL_DIR) {
1092
1111
  if (pipelinePromise !== null) return pipelinePromise;
1093
1112
  pipelinePromise = (async () => {
1113
+ ensureModelSmallFiles(modelDir);
1094
1114
  const { env, pipeline } = await import("@huggingface/transformers");
1095
1115
  env.localModelPath = `${path.dirname(modelDir)}${path.sep}`;
1096
1116
  env.allowLocalModels = true;
@@ -1126,16 +1146,18 @@ const inflightDownloads = /* @__PURE__ */ new Map();
1126
1146
  * - 缺失 → 启动后台下载(不阻塞,单飞)并返回 'downloading';下载失败仅调用
1127
1147
  * warn 记录日志并结束本次尝试,下次调用会重新触发下载(自动重试)。
1128
1148
  * - warn 可选:下载失败时的日志回调(默认静默;apply 注入 ctx.logger.warn)。
1149
+ * - log 可选:下载开始/结束日志回调(默认静默;apply 注入 console.log + ctx.logger.info 双通道)。
1129
1150
  * - fetchImpl 可注入(测试传替身,默认全局 fetch)。
1130
1151
  */
1131
- function ensureModelReady(modelDir = BUNDLED_MODEL_DIR, warn = () => {}, fetchImpl) {
1152
+ function ensureModelReady(modelDir = BUNDLED_MODEL_DIR, warn = () => {}, fetchImpl, log = () => {}) {
1153
+ ensureModelSmallFiles(modelDir);
1132
1154
  if (!needsDownload(modelTargetPath(modelDir))) return Promise.resolve("ready");
1133
1155
  if (!inflightDownloads.has(modelDir)) {
1134
- const task = downloadModel(modelDir, fetchImpl === void 0 ? { log: () => {} } : {
1156
+ const task = downloadModel(modelDir, fetchImpl === void 0 ? { log } : {
1135
1157
  fetchImpl,
1136
- log: () => {}
1158
+ log
1137
1159
  }).then(() => {}).catch((err) => {
1138
- warn("[download-model] 下载失败:" + (err instanceof Error ? err.message : String(err)));
1160
+ warn(`[download-model] 下载失败:${err instanceof Error ? err.message : String(err)}`);
1139
1161
  }).finally(() => {
1140
1162
  inflightDownloads.delete(modelDir);
1141
1163
  });
@@ -1182,7 +1204,7 @@ const DEFAULT_CONFIG = Object.freeze({
1182
1204
  debug: false,
1183
1205
  recallEnabled: true,
1184
1206
  semanticRecallEnabled: true,
1185
- modelDir: BUNDLED_MODEL_DIR
1207
+ modelDir: sharedModelDir()
1186
1208
  });
1187
1209
  /** 合法配置键集合(未知键直接拒绝)。 */
1188
1210
  const CONFIG_KEYS = /* @__PURE__ */ new Set([
@@ -1635,11 +1657,16 @@ function apply(ctx, config) {
1635
1657
  logger.step(`apply 启动:thresholdRatio=${String(resolved.thresholdRatio)} historyMergeRatio=${String(resolved.historyMergeRatio)} compressMaxTokens=${String(resolved.compressMaxTokens)} tailMessageCount=${String(resolved.tailMessageCount)} summaryMode=${resolved.summaryMode} debug=${String(resolved.debug)}`);
1636
1658
  if (resolved.recallEnabled) ctx.tools.register(buildRecallTool(() => ctx.get("toolResultPruner")));
1637
1659
  if (resolved.semanticRecallEnabled) {
1638
- const warnModel = (message) => ctx.logger.warn("dsh-plugin-om: " + message);
1639
- ensureModelReady(resolved.modelDir, warnModel);
1660
+ const warnModel = (message) => ctx.logger.warn(`dsh-plugin-om: ${message}`);
1661
+ /** 下载开始/结束日志:console + 插件日志双通道(失败经 warnModel 输出)。 */
1662
+ const logModel = (message) => {
1663
+ console.log(message);
1664
+ logger.info(message);
1665
+ };
1666
+ ensureModelReady(resolved.modelDir, warnModel, void 0, logModel);
1640
1667
  ctx.tools.register(buildSemanticRecallTool({
1641
1668
  getPruner: () => ctx.get("toolResultPruner"),
1642
- modelStatus: () => ensureModelReady(resolved.modelDir, warnModel),
1669
+ modelStatus: () => ensureModelReady(resolved.modelDir, warnModel, void 0, logModel),
1643
1670
  embedder: (texts) => getEmbedder(resolved.modelDir).then((embed) => embed(texts))
1644
1671
  }));
1645
1672
  }
@@ -2,6 +2,8 @@
2
2
  export declare const EMBEDDING_MODEL_ID = "paraphrase-multilingual-MiniLM-L12-v2";
3
3
  /** transformers.js 转换仓库(Xenova org)中量化 ONNX 文件的相对路径(相对模型目录)。 */
4
4
  export declare const ONNX_REL = "onnx/model_quantized.onnx";
5
+ /** 随 npm 包分发的小模型文件(相对模型目录;onnx 二进制不随包分发,见 tests/package-pack.test.ts)。 */
6
+ export declare const MODEL_SMALL_FILES: string[];
5
7
  /** 下载返回结果。 */
6
8
  export type DownloadResult = {
7
9
  downloaded: boolean;
@@ -31,7 +33,9 @@ export type ModelFetch = (url: string) => Promise<{
31
33
  }>;
32
34
  /**
33
35
  * 下载模型到 <modelDir>/onnx/model_quantized.onnx。
34
- * 可注入 fetchImpl / log(供测试);失败时清理临时文件并抛出。
36
+ * 开始/结束(含跳过)经 log 回调输出(默认 console.log);失败时清理临时文件,
37
+ * 并在错误消息中附带镜像建议(直连受限时设置 HF_ENDPOINT=https://hf-mirror.com)。
38
+ * 可注入 fetchImpl / log(供测试)。
35
39
  */
36
40
  export declare function downloadModel(modelDir: string, { force, fetchImpl, log, }?: {
37
41
  force?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"model-download.d.ts","sourceRoot":"","sources":["../src/model-download.ts"],"names":[],"mappings":"AAgBA,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,0CAA0C,CAAC;AAE1E,gEAAgE;AAChE,eAAO,MAAM,QAAQ,8BAA8B,CAAC;AAEpD,cAAc;AACd,MAAM,MAAM,cAAc,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,EAAE,GAAE,MAA2B,EAC/B,GAAG,GAAE,MAAiB,EACtB,IAAI,GAAE,MAA4D,GACjE,MAAM,CAER;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAiB,GAAG,MAAM,CAEhF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,EAAE,KAAa,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1C,OAAO,CAQT;AAED,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACrC,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,EACE,KAAa,EACb,SAAiB,EACjB,GAAiB,GAClB,GAAE;IACD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,GACL,OAAO,CAAC,cAAc,CAAC,CAwBzB"}
1
+ {"version":3,"file":"model-download.d.ts","sourceRoot":"","sources":["../src/model-download.ts"],"names":[],"mappings":"AAmBA,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,0CAA0C,CAAC;AAE1E,gEAAgE;AAChE,eAAO,MAAM,QAAQ,8BAA8B,CAAC;AAEpD,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,UAK7B,CAAC;AAEF,cAAc;AACd,MAAM,MAAM,cAAc,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,EAAE,GAAE,MAA2B,EAC/B,GAAG,GAAE,MAAiB,EACtB,IAAI,GAAE,MAA4D,GACjE,MAAM,CAER;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAiB,GAAG,MAAM,CAEhF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,EAAE,KAAa,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1C,OAAO,CAQT;AAED,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACrC,CAAC,CAAC;AAEH;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,EACE,KAAa,EACb,SAAiB,EACjB,GAAiB,GAClB,GAAE;IACD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,GACL,OAAO,CAAC,cAAc,CAAC,CA6BzB"}
@@ -9,7 +9,7 @@
9
9
  * - disable:关闭自动压缩(compress.ts 早退,不发起摘要调用)。
10
10
  *
11
11
  * 提示词不内嵌消息全文(fork 模式完整历史随请求传入;new 模式由 compress.ts 渲染
12
- * 区间传入);新消息起始 index 与中断标记由插件从日志计算后内嵌——AI 按「完整消息」的
12
+ * 区间传入);新消息起始 index 由插件从日志计算后内嵌——AI 按「完整消息」的
13
13
  * index 编号(三类定义见 log-index.ts),recall 用同一套编号定位。token usage 从流式
14
14
  * 响应的 usage chunk 提取,归入主会话记录。仅主会话生效(index.ts 守卫)。
15
15
  */
@@ -17,15 +17,13 @@ import type { SummaryMode } from './config.ts';
17
17
  import type { Agent, Context, Session, TokenUsage } from './types.ts';
18
18
  import { type RoutedTarget } from './utils.ts';
19
19
  /** 观察者 persona:只针对未压缩消息产出观察日志,不用工具、不展示思考。 */
20
- export declare const OBSERVER_PERSONA = "\u4F60\u662F dsh-plugin-om \u7684\u4E0A\u4E0B\u6587\u89C2\u5BDF\u8005\uFF08Observer\uFF0C\u673A\u5236\u53C2\u8003 Mastra Observational Memory\uFF09\uFF1A\u628A\u4F1A\u8BDD\u4E2D\u5C1A\u672A\u538B\u7F29\u7684\u6D88\u606F\u538B\u7F29\u4E3A\u4E00\u4EFD\u89C2\u5BDF\u65E5\u5FD7\u3002\u4E0D\u7528\u5DE5\u5177\u3001\u4E0D\u5C55\u793A\u601D\u8003\u3001\u4E0D\u8BC4\u4EF7\u4EE3\u7801\u3001\u4E0D\u8F93\u51FA\u591A\u4F59\u6587\u5B57\u3002";
20
+ export declare const OBSERVER_PERSONA = "\u628A\u4F1A\u8BDD\u4E2D\u5C1A\u672A\u538B\u7F29\u7684\u6D88\u606F\u538B\u7F29\u4E3A\u4E00\u4EFD\u89C2\u5BDF\u65E5\u5FD7\u3002\u4E0D\u7528\u5DE5\u5177\u3001\u4E0D\u5C55\u793A\u601D\u8003\u3001\u4E0D\u8BC4\u4EF7\u4EE3\u7801\u3001\u4E0D\u8F93\u51FA\u591A\u4F59\u6587\u5B57\u3002";
21
21
  /** 反思者 persona:只精简合并当前摘要,不用工具、不展示思考。 */
22
- export declare const REFLECTOR_PERSONA = "\u4F60\u662F dsh-plugin-om \u7684\u4E0A\u4E0B\u6587\u53CD\u601D\u8005\uFF08Reflector\uFF0C\u673A\u5236\u53C2\u8003 Mastra Observational Memory\uFF09\uFF1A\u628A\u5F53\u524D <om-history> \u538B\u7F29\u65E5\u5FD7\u7CBE\u7B80\u5408\u5E76\u4E3A\u4E00\u4EFD\u66F4\u7D27\u51D1\u7684\u65E5\u5FD7\u3002\u4E0D\u7528\u5DE5\u5177\u3001\u4E0D\u5C55\u793A\u601D\u8003\u3001\u4E0D\u8F93\u51FA\u591A\u4F59\u6587\u5B57\u3002";
23
- /** 观察提示词参数(起始 index/中断标记由 compress.ts 从日志计算后传入)。 */
22
+ export declare const REFLECTOR_PERSONA = "\u628A\u5F53\u524D <om-history> \u538B\u7F29\u65E5\u5FD7\u7CBE\u7B80\u5408\u5E76\u4E3A\u4E00\u4EFD\u66F4\u7D27\u51D1\u7684\u65E5\u5FD7\u3002\u4E0D\u7528\u5DE5\u5177\u3001\u4E0D\u5C55\u793A\u601D\u8003\u3001\u4E0D\u8F93\u51FA\u591A\u4F59\u6587\u5B57\u3002";
23
+ /** 观察提示词参数(起始 index compress.ts 从日志计算后传入)。 */
24
24
  export type ObservePromptOptions = {
25
25
  /** 本次要压缩的新消息起始完整消息 index(AI 从该序号开始编号)。 */
26
26
  startIndex: number;
27
- /** 中断标记行(aborted/interrupted 轮次)。 */
28
- interruptions: string[];
29
27
  /** 是否存在旧摘要(决定「追加到上次产物末尾」的表述)。 */
30
28
  hasOldHistory: boolean;
31
29
  /** 摘要模式:fork=完整历史随请求传入;new=被压缩消息渲染为输入。 */
@@ -35,9 +33,9 @@ export type ObservePromptOptions = {
35
33
  * 构建观察指令主体:任务声明(fork:停止任务/禁止工具;new:说明总结日志)+ 模式相关
36
34
  * 的上下文定位与压缩范围(fork:上方完整会话记录——尾部已在输入中实际截断,提示词不含
37
35
  * 尾部规则;new:下方消息即压缩对象)+ 完整消息与 index 定义(三类合并规则、起始编号)+
38
- * 规则(用户消息完整保留原文 / AI 消息模块化压缩——工具调用按目的聚合——不限于 run_code /
39
- * 倾向于新消息 / 中断标注 / 未完成写进度与下一步)+ 输出格式(合法 XML)+ 中断标记 +
40
- * 追加说明。persona 由调用方拼接到指令开头。
36
+ * 规则(用户消息完整保留原文 / AI 消息按模块压缩 / 本指令不入日志 / index/start/end 连续 /
37
+ * 倾向于新消息 / 未完成写进度与下一步)+ 输出格式(合法 XML)+ 追加说明。
38
+ * persona 由调用方拼接到指令开头。
41
39
  */
42
40
  export declare function buildObservePrompt(options: ObservePromptOptions): string;
43
41
  /** 构建反思指令主体:任务声明(fork:停止任务/禁止工具;new:说明总结日志)+ 精简合并
@@ -61,7 +59,7 @@ export declare function renderMessages(session: Session, seqs: readonly number[]
61
59
  /** 日志最小有效长度:<om-history> 中间内容小于该长度视为不合法(C 段校验)。 */
62
60
  export declare const MIN_HISTORY_LENGTH = 10;
63
61
  /** 产出日志后插入首个 <om-history> 后的格式说明(XML 注释,避免被误读为日志条目)。 */
64
- export declare const HISTORY_FORMAT_NOTE = "<!-- \u672C\u65E5\u5FD7\u4EE5\u300C\u5B8C\u6574\u6D88\u606F\u300D\u4E3A\u5B9A\u4F4D\u5355\u4F4D\uFF1A\u5B8C\u6574\u6D88\u606F\u5206\u4E09\u7C7B\uFF08\u7528\u6237\u6D88\u606F / AI \u6587\u672C / \u5355\u4E2A\u5DE5\u5177\u8C03\u7528\u53CA\u5176\u7ED3\u679C\uFF09\uFF0C\u5404\u5360\u4E00\u4E2A index\uFF08\u4ECE 0 \u8D77\u3001\u4F1A\u8BDD\u5185\u5168\u5C40\u7A33\u5B9A\uFF09\uFF1B<user_message index=\"N\"> \u4E0E <assistant index=\"N\"> \u5B9A\u4F4D\u5355\u6761\u5B8C\u6574\u6D88\u606F\uFF0C<assistant start=\"A\" end=\"B\"> \u5B9A\u4F4D\u8FDE\u7EED\u533A\u95F4\uFF08\u542B\u4E24\u7AEF\uFF09\u3002\u9700\u8981\u539F\u6587\u65F6\u7528 recall(start, end) \u6309 index \u56DE\u770B\u3002 -->";
62
+ export declare const HISTORY_FORMAT_NOTE = "<!-- \u5B8C\u6574\u6D88\u606F\u5206\u4E09\u7C7B\uFF08\u7528\u6237\u6D88\u606F / AI \u6587\u672C / \u5355\u4E2A\u5DE5\u5177\u8C03\u7528\u53CA\u5176\u7ED3\u679C\uFF09\uFF0C\u5404\u5360\u4E00\u4E2A index\uFF08\u4ECE 0 \u8D77\u3001\u4F1A\u8BDD\u5185\u5168\u5C40\u7A33\u5B9A\uFF09\uFF1B<user_message index=\"N\"> \u4E0E <assistant index=\"N\"> \u8868\u793A\u5355\u6761\u5B8C\u6574\u6D88\u606F\uFF0C<assistant start=\"A\" end=\"B\"> \u8868\u793A\u591A\u6761\u5B8C\u6574\u6D88\u606F\uFF0Cstart/end\u662F\u9996\u5C3E\u6D88\u606F\u7684index -->";
65
63
  /**
66
64
  * 从 AI 摘要输出中提取合法日志(不信任 AI 的总结结果):
67
65
  * - 取首个 <om-history> 到最后一个 </om-history>(含两个首尾)切为日志;
@@ -1 +1 @@
1
- {"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AACnF,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,YAAY,CAAC;AAErD,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,kbACyF,CAAC;AAEvH,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,6ZACiG,CAAC;AAEhI,oDAAoD;AACpD,MAAM,MAAM,oBAAoB,GAAG;IACjC,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iCAAiC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,0CAA0C;IAC1C,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CA0DxE;AAED;qEACqE;AACrE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA4C5D;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,CAAC;AA4CF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAehF;AA4DD,mDAAmD;AACnD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,mrBACmM,CAAC;AAEpO;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB5D;AAED,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAKtC;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+EvC"}
1
+ {"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AACnF,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,YAAY,CAAC;AAErD,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,yRACqB,CAAC;AAEnD,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,mQAC4B,CAAC;AAE3D,gDAAgD;AAChD,MAAM,MAAM,oBAAoB,GAAG;IACjC,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,0CAA0C;IAC1C,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAuDxE;AAED;qEACqE;AACrE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA+C5D;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,CAAC;AA4CF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAehF;AA4DD,mDAAmD;AACnD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,4hBAC+J,CAAC;AAEhM;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB5D;AAED,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAKtC;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+EvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-om",
3
- "version": "0.0.7",
3
+ "version": "0.0.11",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -42,7 +42,7 @@
42
42
  "build": "tsdown && tsc -p tsconfig.build.json",
43
43
  "download:model": "node scripts/download-model.mjs",
44
44
  "check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build",
45
- "release": "pnpm format && npm version patch && node scripts/release-archive.mjs && git push && git push --tags",
45
+ "release": "pnpm check && pnpm format && npm version patch && node scripts/release-archive.mjs && git push && git push --tags",
46
46
  "typecheck": "tsc --noEmit",
47
47
  "test": "vitest run",
48
48
  "build:types": "tsc -p tsconfig.build.json",