dsh-plugin-om 0.0.4 → 0.0.6

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,23 +1,30 @@
1
1
  # dsh-plugin-om
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/dsh-plugin-om.svg)](https://www.npmjs.com/package/dsh-plugin-om)
4
+
3
5
  在 DSH 里应用 [Observational Memory](https://mastra.ai/research/observational-memory) 风格的上下文管理策略
4
6
 
5
7
  ## 工作原理
6
8
 
7
- 1. 在未压缩消息超过阈值后摘要
8
- 2. 摘要会替换原始消息,并追加至现有摘要
9
- 3. 在摘要超过阈值后,重新摘要
10
- 4. 摘要过程中保留关键的message_id,允许模型精确recall
9
+ 1. 在未压缩消息超过阈值后摘要:压缩**尾部之前**的消息并用摘要替换(尾部 `tailMessageCount` 条不压缩、不被替换、不进日志)
10
+ 2. 摘要替换原始消息,并以**多个 `<om-history>` 块按序拼接**的方式追加至现有摘要(旧块原样保留,新块追加在末尾)
11
+ 3. 摘要超过阈值后重新摘要(反思:精简合并现有日志块)
12
+ 4. 摘要输出为**合法 XML 日志**(`<om-history>` 内 `<user_message id>` 完整保留用户原文、`<assistant last_id>` 聚合 AI 模块);插件不信任 AI 输出,取首个 `<om-history>` 到最后一个 `</om-history>`(含首尾)切为日志,找不到或中间内容过短视为不合法并按失败重试;产出后插入格式说明注释
13
+ 5. 摘要过程中保留 message_id(`<user_message id>` / `<assistant last_id>`),允许模型精确 recall
14
+ 6. 压缩在 `agent/pre-step` 触发(**turn 中间即可**,无需等待轮次结束):摘要直连 LLM,模式见[摘要模式](#摘要模式)——`fork`(缺省)复用主会话请求前缀(系统提示词 + 截至尾部前的消息 + 末尾指令),充分利用 provider 前缀缓存;`new` 只注入被压缩消息(XML 包裹);`disable` 关闭自动压缩
15
+ 7. 提供语义召回(recall-semantic):按自然语言在全部消息日志中检索,被压缩/遮蔽的消息也可按语义找回
11
16
 
12
17
  ### 注意
13
18
 
14
19
  - recall 不截断,建议保留 `tool-result-pruner`
15
20
  - 默认上下文压缩插件 `compaction-basic` 到达阈值后会自动摘要,不建议和此插件一起使用
21
+ - recall-semantic 使用本地多语言嵌入模型(paraphrase-multilingual-MiniLM-L12-v2,量化 ONNX),完全离线;首次调用时加载模型,之后复用。模型二进制(~113MB)不进入 git 仓库,改为**运行时按需下载**:启用 recall-semantic 且模型缺失时,插件启动即后台自动下载(不阻塞),未就绪时工具会告知模型(见[依赖策略](#依赖策略))
16
22
 
17
23
  ### 依赖策略
18
24
 
19
- - 以type-only的方式引用第三方库
20
25
  - 复用dsh宿主提供的依赖,如 cordis / dsh-tools / zod 等
26
+ - 例外:recall-semantic 的本地嵌入需要运行时依赖 `@huggingface/transformers`(transformers.js v4 + onnxruntime-node),模型小文件(config/tokenizer 等)随 npm 包分发(`models/`),onnx 二进制不做构建/发布时下载
27
+ - 模型二进制:量化 ONNX 约 113MB,超过 GitHub 单文件 100MB 限制,**不进入 git 仓库**。改为**运行时按需下载**:仅当配置键 `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` 走镜像
21
28
 
22
29
  ## 安装与启用
23
30
 
@@ -34,15 +41,26 @@
34
41
  dsh plugin --profile <profile> add dsh-plugin-om
35
42
  ```
36
43
 
37
- 无需重启
44
+ 需要重启dsh
38
45
 
39
46
  可以通过`dsh --profile <profile> --dump-config`审查配置是否正确
40
47
 
48
+ 如果需要覆盖默认配置,打开 `$DSH_HOME/profiles/<profile>/cordis.patch.yml` 删除里面的空数组,加入
49
+
50
+ ```yaml
51
+ - id: dsh-plugin-om
52
+ config:
53
+ thresholdRatio: 0.2
54
+ # 其他配置参考下文
55
+ ```
56
+
57
+ 不需要重启 可以热更新
58
+
41
59
  ### 开发插件
42
60
 
43
- 运行`pnpm dev`,等待`dist/index.mjs`构筑完毕
61
+ 运行`pnpm dev`,等待`dist/index.mjs`构筑完毕(本地嵌入模型可在运行时自动下载;如需提前预下载用`pnpm run download:model`,已存在则跳过)
44
62
 
45
- 打开 `$DSH_HOME/profiles/<profile>/cordis.patch.yml` 删除里面的空数组,加入
63
+ `cordis.patch.yml` 里加入
46
64
 
47
65
  ```yaml
48
66
  - insert:
@@ -58,33 +76,66 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
58
76
 
59
77
  而`web-profile`里定义了多个"预设agent"`preset-agent` 这里主要说的是`preset-agent`的问题
60
78
 
61
- `preset-agent`配置里自带一个`compaction-basic`。由于配置注入的顺序是层级低的覆盖层级高的,所以`cordis.patch.yml`对`compaction-basic`的禁用不会生效
79
+ `preset-agent`配置里自带一个`compaction-basic`。由于配置注入的顺序是层级低的覆盖层级高的,所以`cordis.patch.yml`对`compaction-basic`的禁用不会生效。
80
+
81
+ 此外,这个插件位于`preset-agent`的定义而非额外的安装,也无法通过`dsh plugin`卸载。
62
82
 
63
83
  解决方案:
64
84
 
85
+ - 直接改`preset-agent`的定义(不推荐)
65
86
  - 定义不含`compaction-basic`的`preset-agent`
66
87
  - `compaction-basic`压缩阈值是80%上下文窗口,而只需要确保OM的配置中,`thresholdRatio`+`historyMergeRatio`<0.8,理论上没到强制摘要就会被OM压缩了(默认值满足这一条件)
67
88
 
68
89
  ## 插件配置项
69
90
 
70
- | 键 | 默认 | 含义 |
71
- | ------------------- | ------ | -------------------------------------------- |
72
- | `thresholdRatio` | `0.5` | 观察阈值:未压缩消息 ≥ 窗口 × 该比例触发压缩 |
73
- | `historyMergeRatio` | `0.2` | 反思阈值:摘要 ≥ 窗口 × 该比例触发精简合并 |
74
- | `compressMaxTokens` | `4096` | 单次摘要(观察/反思调用)生成上限 |
75
- | `tailMessageCount` | `10` | 压缩后保留的未压缩消息条数 |
91
+ | 键 | 默认 | 含义 |
92
+ | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
93
+ | `thresholdRatio` | `0.5` | 观察阈值:未压缩消息 ≥ 窗口 × 该比例触发压缩 |
94
+ | `historyMergeRatio` | `0.2` | 反思阈值:摘要 ≥ 窗口 × 该比例触发精简合并 |
95
+ | `compressMaxTokens` | `4096` | 单次摘要(观察/反思调用)生成上限 |
96
+ | `tailMessageCount` | `10` | 尾部保留的不压缩消息条数(不压缩、不被替换、不进摘要日志) |
97
+ | `modelDir` | 打包模型 | recall-semantic 嵌入模型目录(默认插件内打包的本地模型;可指向自定义目录)。onnx 缺失时运行时自动下载到该目录 |
98
+ | `summaryMode` | `fork` | 摘要模式:`fork`(缺省)/ `new` / `disable`(关闭自动压缩);非法值在插件加载时报错(见[摘要模式](#摘要模式)) |
99
+ | `debug` | dev | 压缩流程步骤级(debug)日志开关:`true` 强制开启、`false` 强制关闭;缺省按 `NODE_ENV !== 'production'` 判定(dev/test 输出,生产隐藏)。**失败日志不受此开关影响,始终输出** |
100
+ | `recallEnabled` | `true` | 是否注册 `recall` 工具(`false` 时禁用,不注册) |
101
+ | `semanticRecallEnabled` | `true` | 是否注册 `recall-semantic` 工具(`false` 时禁用,不注册、不触发模型下载) |
102
+
103
+ > 数值键(`thresholdRatio` / `historyMergeRatio` / `compressMaxTokens` / `tailMessageCount`)**不做取值区间限制**(如阈值不再限定 0.01–1):用户提供的值按原样接受(仅校验为有限数,整数键另校验整数性),便于调试时设置任意值。
104
+
105
+ ### 摘要模式
106
+
107
+ 摘要调用由配置键 `summaryMode` 控制(缺省 `fork`;非法值在插件加载时报错):
108
+
109
+ - `fork`(缺省):fork 会话风格——复用主会话请求前缀——`system`/`tools` 取自主会话上次请求,`messages` = 完整派生历史**从尾部之前实际截断**(尾部 `tailMessageCount` 条不注入、不进日志)+ 末尾追加指令 user 消息,充分利用 provider 前缀缓存(与宿主 `compaction-basic` 同款策略)。
110
+ - `new`:新开会话风格——指令(persona + 规则)作为 system 提示词,只注入本次要压缩的消息作为 user 消息输入模型压缩(不注入旧压缩日志、不注入尾部)。
111
+ - `disable`:关闭自动压缩(观察/反思均不触发;recall 工具仍由 `recallEnabled` / `semanticRecallEnabled` 独立控制)。
112
+
113
+ 示例(禁用 recall-semantic,保留 recall;生产环境强制输出步骤日志):在 `cordis.patch.yml` 的插件配置中加入
114
+
115
+ ```yaml
116
+ - id: dsh-plugin-om
117
+ config:
118
+ semanticRecallEnabled: false
119
+ debug: true
120
+ ```
121
+
122
+ ### 日志与摘要重试
123
+
124
+ - 压缩流程(路由 / 模型容量 / 阈值判定 / 区间计算 / 摘要调用 / 提交各步)逐步输出步骤级日志(`debug` 级,dev 环境默认可见,见配置键 `debug`)。
125
+ - 摘要调用失败(抛异常 / 空输出 / 非 `stop` 结束)均记录日志并**自动重试,总共最多尝试 3 次**;每次失败记录尝试次数与原因,重试耗尽后记录最终失败日志。失败日志不受 dev 开关影响,始终输出。
76
126
 
77
127
  ## npm 命令
78
128
 
79
- | 命令 | 作用 |
80
- | --------------------------- | ----------------------------------------- |
81
- | `pnpm check` | typecheck + lint + test + build |
82
- | `pnpm typecheck` | TypeScript 类型检查 |
83
- | `pnpm lint` / `pnpm format` | 代码检查 / 格式化 |
84
- | `pnpm test` | vitest 单元测试 |
85
- | `pnpm build` | |
86
- | `pnpm dev` | 自动打包 |
87
- | `pnpm run release` | CHANGELOG 归档 + 版本号更新 + 打 tag 推送 |
129
+ | 命令 | 作用 |
130
+ | --------------------------- | --------------------------------------------------------------------------------- |
131
+ | `pnpm check` | typecheck + lint + test + build |
132
+ | `pnpm typecheck` | TypeScript 类型检查 |
133
+ | `pnpm lint` / `pnpm format` | 代码检查 / 格式化 |
134
+ | `pnpm test` | vitest 单元测试 |
135
+ | `pnpm run download:model` | 手动预下载本地嵌入模型 ONNX(已存在跳过,`--force` 重下;运行时也会按需自动下载) |
136
+ | `pnpm build` | |
137
+ | `pnpm dev` | 自动打包 |
138
+ | `pnpm run release` | CHANGELOG 归档 + 版本号更新 + 打 tag 推送 |
88
139
 
89
140
  ## 调用链和文件地图
90
141
 
@@ -94,25 +145,33 @@ src/
94
145
  ├── index.ts # 打包入口(tsdown entry),导出 name / inject / apply
95
146
  │ apply(ctx, config) 三条主线(标注对应实现文件):
96
147
  │ ├─ ① resolveConfig(config) ──▶ config.ts # 配置默认值合并 + 逐键校验(留空回退默认,冻结返回)
97
- │ ├─ ② ctx.tools.register(buildRecallTool(() => ctx.get('toolResultPruner')))
148
+ │ ├─ ② recallEnabled 时 ctx.tools.register(buildRecallTool(() => ctx.get('toolResultPruner')))
98
149
  │ │ └─▶ recall.ts # recall 工具:按 message_id 回看区间(超大结果由 pruner 裁剪)
99
- └─事件接线(仅主会话生效)
100
- └─ ctx.on('agent/pre-step') → compress.ts # maybeCompress:两级压缩阻塞串行(先反思后观察)
101
- ├─ reflectPass → summarize.ts # 摘要 窗口 × historyMergeRatio:fork 精简合并 <om-history>
102
- └─ observePass → summarize.ts # 未压缩消息 窗口 × thresholdRatio:fork 观察日志 → 追加 + 替换
103
- ├── constants.ts # 共享常量(PLUGIN_LABEL / HISTORY_TAG / CLAIM_EVENT)
150
+ ├─semanticRecallEnabled 时 ctx.tools.register(buildSemanticRecallTool({ getPruner, modelStatus, embedder }))
151
+ │ └─▶ semantic-recall.ts # recall-semantic 工具:本地嵌入按语义检索全部消息日志(含被压缩/遮蔽)
152
+ │ └─▶ embedding.ts # 本地 ONNX 嵌入:ensureModelReady 运行时按需下载(不阻塞/单飞)+ 懒加载 + 批量 embed + cosine
153
+ │ └─▶ model-download.ts # 模型下载原语(URL/跳过判定/原子落盘;dev CLI 复用)
154
+ │ └─ 事件接线(仅主会话生效)
155
+ │ └─ ctx.on('agent/pre-step') → compress.ts # maybeCompress:两级压缩阻塞串行(先反思后观察;turn 中间即可触发)
156
+ │ ├─ reflectPass → summarize.ts # 摘要 ≥ 窗口 × historyMergeRatio:摘要调用精简合并 <om-history>
157
+ │ ├─ observePass → summarize.ts # 未压缩消息 ≥ 窗口 × thresholdRatio:摘要调用观察日志 → 追加 + 替换
158
+ │ └─ 提交 → compress.ts # compaction/start → summary → 替换消息(checkpoint) → end;usage 归入主会话
159
+ ├── constants.ts # 共享常量(PLUGIN_LABEL / HISTORY_TAG / COMPACT_CHECKPOINT_PLUGIN)
104
160
  ├── types.ts # type-only:宿主类型再导出 + 领域类型(MessageNode / MessageIndex)
105
- ├── config.ts # 配置默认值 / 校验(缺省、null、空串回退默认值)
161
+ ├── config.ts # 配置默认值 / 校验(缺省、null、空串回退默认值;数值键/布尔键/summaryMode/modelDir)
106
162
  ├── utils.ts # 零依赖工具函数(配置校验 / 文本渲染 / 主会话判定 / 路由解析)
107
163
  ├── log-index.ts # 消息索引(message_id → 消息事件;recall 消费)
108
- ├── summarize.ts # 观察/反思 persona + 提示词 + fork 摘要子会话
164
+ ├── embedding.ts # 本地 ONNX 嵌入(@huggingface/transformers + 本地模型;运行时按需下载编排 / 懒加载 / 批量 / cosine)
165
+ ├── model-download.ts # 模型下载原语(modelSourceUrl / needsDownload / 原子落盘;运行时与 dev CLI 共用)
166
+ ├── summarize.ts # 观察/反思 persona + 提示词 + 直连 ctx.llm.stream() 摘要(fork/new 双模式;extractSummaryLog 提取校验;流式 usage 归入主会话)
109
167
  ├── recall.ts # recall 工具
110
- └── compress.ts # 两级自动压缩(测量 / 区间计算 / 中断扫描 / 对照表 / 替换 + 影子价格认领)
111
- scripts/ # release-archive.mjs(CHANGELOG 归档)
112
- tests/ # vitest 单元测试(52 例)
168
+ ├── semantic-recall.ts # recall-semantic 工具(query 语义检索 + 区间限定 + 回退全量 + 匹配说明)
169
+ └── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / 中断扫描 / 对照表 / source 标记判定摘要消息 / compaction/* 生命周期事件 + checkpoint 替换)
170
+ models/
171
+ └── paraphrase-multilingual-MiniLM-L12-v2/ # 嵌入模型目录(小文件随包分发;onnx 二进制由运行时按需下载到此处,不进 git)
172
+ scripts/ # release-archive.mjs(CHANGELOG 归档)/ download-model.mjs(开发手动预下载 CLI)
173
+ tests/ # vitest 单元测试(135 例)
174
+ .dsh/skills/ # 项目级 skill(feature-defect-workflow:需求/缺陷完成工作流)
113
175
  ```
114
176
 
115
- ## 开发计划
116
-
117
- - 在消息记录/轨迹中添加OM结果
118
- - 将OM会话的token加入主会话中
177
+ ## TODO
@@ -1,4 +1,5 @@
1
1
  import type { Agent, Context, PluginConfig, Session } from './types.ts';
2
+ import { type RoutedTarget } from './utils.ts';
2
3
  /** 历史文本 token 估算:4 字符 ≈ 1 token(与宿主 dsh-token-meter 启发式一致)。 */
3
4
  export declare function estimateTextTokens(text: string): number;
4
5
  /** token 估算器的结构类型(仅需 estimateMessage;避免依赖完整 TokenMeter 接口)。 */
@@ -16,8 +17,17 @@ export declare function findLatestHistory(session: Session): {
16
17
  seq: number;
17
18
  } | undefined;
18
19
  /**
19
- * 观察压缩区间:尾部保留 tailCount 条消息不压缩,区间封顶在最后一个已结束 turn
20
- * 表层节点(fork seed 截断于最后一个 turn/end,当前 turn 消息不可压缩)。
20
+ * 判定表层节点 seq 之后的切点是否 tool-call/result 配对平衡(与宿主
21
+ * dsh-compaction toolPairingBalancedAfter 同语义):按表层顺序折叠未闭合的
22
+ * 工具调用数,处理到 seq 后计数为 0 即平衡。pre-step 时日志 call-result 完备,
23
+ * 该检查作为区间边界的安全网(防止把助手 tool-call 与其结果切到两侧)。
24
+ */
25
+ export declare function isPairBalancedAfter(session: Session, seq: number): boolean;
26
+ /**
27
+ * 观察压缩区间:pre-step 触发时日志 call-result 完备,区间不再受 turn/end 封顶——
28
+ * 头部 → 表层长度-1-tailCount(尾部保留 tailCount 条不压缩),当前 turn 中已完备的
29
+ * 消息同样可压缩;区间终点回退到 tool-call/result 配对平衡点(不切段)。
30
+ * lastEndSeq 仅为中断扫描提供最后一个已结束 turn 的边界(无则 -1)。
21
31
  */
22
32
  export declare function computeCompressRange(session: Session, tailCount: number): {
23
33
  start: number;
@@ -41,20 +51,20 @@ export declare function extractHistoryText(session: Session, shadowedSeqs: reado
41
51
  } | undefined;
42
52
  /**
43
53
  * message_id 对照表:遮蔽区间内消息事件按表层顺序产出 id 行(插件自产 user/message
44
- * 如运行时上下文快照与 <om-history> 不入表;观察子会话据此产出正确的 message_id)。
54
+ * 如运行时上下文快照与 <om-history> 不入表;观察摘要据此产出正确的 message_id)。
45
55
  * 按表层顺序(shadowedSeqs)扫描:与 extractHistoryText 同理,seq 区间扫描会漏。
46
56
  */
47
57
  export declare function buildMessageIdTable(session: Session, shadowedSeqs: readonly number[]): string[];
48
58
  /**
49
- * 反思:摘要 tokens ≥ 窗口 × historyMergeRatio 时,fork 子会话精简合并摘要,
59
+ * 反思:摘要 tokens ≥ 窗口 × historyMergeRatio 时,摘要调用精简合并摘要,
50
60
  * 替换单个 <om-history> 节点。失败不产生部分替换。
51
61
  */
52
- export declare function reflectPass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, window: number, signal?: AbortSignal): Promise<void>;
62
+ export declare function reflectPass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, window: number, target: RoutedTarget, signal?: AbortSignal): Promise<void>;
53
63
  /**
54
- * 观察:未压缩消息 tokens ≥ 窗口 × thresholdRatio 时,fork 子会话把未压缩消息压缩为
64
+ * 观察:未压缩消息 tokens ≥ 窗口 × thresholdRatio 时,摘要调用把未压缩消息压缩为
55
65
  * 观察日志,追加到旧摘要并替换被压缩消息区间。失败不产生部分替换。
56
66
  */
57
- export declare function observePass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, window: number, tailCount: number, signal?: AbortSignal): Promise<void>;
67
+ export declare function observePass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, window: number, tailCount: number, target: RoutedTarget, signal?: AbortSignal): Promise<void>;
58
68
  /**
59
69
  * 压力检查 + 两级压缩:先反思(压缩过往摘要,有必要才做),后观察(压缩新消息,
60
70
  * 有必要才做)。在 pre-step 阻塞串行执行(避免压缩失败或重复压缩)。仅主会话生效。
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAgB,MAAM,YAAY,CAAC;AAGtF,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAYD,+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;;;GAGG;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,CA4BxF;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;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CA6B/F;AA8CD;;;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,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Cf;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,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Ef;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,CAyBf"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAiCA,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;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CA6B/F;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,CAyIf;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
@@ -1,18 +1,41 @@
1
+ /**
2
+ * 摘要模式:
3
+ * - fork(缺省):fork 会话风格——复用主会话请求前缀(system/tools 与完整派生历史),
4
+ * 充分利用 provider 前缀缓存;
5
+ * - new:新开会话风格——只注入本次要压缩的消息(XML 包裹),指令作为 system;
6
+ * - disable:关闭自动压缩(观察/反思均不触发)。
7
+ */
8
+ export type SummaryMode = 'fork' | 'new' | 'disable';
1
9
  /** 插件配置项(全部可选覆盖,未给出或留空的键用默认值)。 */
2
10
  export type PluginConfig = {
3
11
  /** 压力阈值比例:压力 ≥ 窗口 × 该比例时触发自动压缩。 */
4
12
  thresholdRatio: number;
5
- /** 反思阈值比例:摘要(<om-history> 内容)≥ 窗口 × 该比例时由反思子会话精简合并。 */
13
+ /** 反思阈值比例:摘要(<om-history> 内容)≥ 窗口 × 该比例时由反思摘要调用精简合并。 */
6
14
  historyMergeRatio: number;
7
15
  /** 单次摘要(合并调用)生成上限(LLM maxTokens)。 */
8
16
  compressMaxTokens: number;
9
- /** 压缩边界:其后不压缩消息数下限(正整数)。 */
17
+ /** 压缩边界:其后不压缩消息数下限(正整数,尾部保留)。 */
10
18
  tailMessageCount: number;
19
+ /** 摘要模式(缺省 fork;非法值在插件加载时报错)。 */
20
+ summaryMode: SummaryMode;
21
+ /** 步骤级(debug)日志开关:true 强制开启、false 强制关闭;缺省按 NODE_ENV !== 'production' 判定。 */
22
+ debug: boolean;
23
+ /** 是否注册 recall 工具(缺省 true;false 时不注册)。 */
24
+ recallEnabled: boolean;
25
+ /** 是否注册 recall-semantic 工具(缺省 true;false 时不注册、不触发模型下载)。 */
26
+ semanticRecallEnabled: boolean;
27
+ /** 语义召回嵌入模型目录(默认插件打包的本地模型;可指向自定义模型目录)。 */
28
+ modelDir: string;
11
29
  };
12
- /** 默认配置(冻结对象,resolveConfig 合并的基底)。 */
30
+ /** 默认配置(冻结对象,resolveConfig 合并的基底;debug 缺省值在解析时按 NODE_ENV 判定)。 */
13
31
  export declare const DEFAULT_CONFIG: Readonly<PluginConfig>;
14
32
  /**
15
- * 解析合并配置:校验未知键与数值类型,返回冻结的完整配置。
33
+ * 解析摘要模式配置值:缺省 / 空串回退 fork;仅接受 'fork'/'new'/'disable';
34
+ * 其余值抛错(配置错误须立即可见)。
35
+ */
36
+ export declare function resolveSummaryMode(raw: unknown): SummaryMode;
37
+ /**
38
+ * 解析合并配置:校验未知键与数值/字符串类型,返回冻结的完整配置。
16
39
  * 允许所有配置留空——缺省 / null / 空串的键回退默认值,未给出的键亦取默认值。
17
40
  */
18
41
  export declare function resolveConfig(raw?: unknown): Readonly<PluginConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,4BAA4B;IAC5B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,sCAAsC;AACtC,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAKhD,CAAC;AA6BH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CAiBnE"}
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"}
@@ -5,6 +5,6 @@
5
5
  export declare const PLUGIN_LABEL = "dsh-plugin-om";
6
6
  /** 压缩日志标签名:<om-history>...</om-history> 包裹观察/反思日志块。 */
7
7
  export declare const HISTORY_TAG = "om-history";
8
- /** 影子价格认领事件类型:token-meter 据此识别被替换(遮蔽)的表层节点。 */
9
- export declare const CLAIM_EVENT = "compaction/prune";
8
+ /** 宿主压缩 checkpoint 标记的 plugin 名(dsh-compaction-basic COMPACT_CHECKPOINT_MARKER.plugin)。 */
9
+ export declare const COMPACT_CHECKPOINT_PLUGIN = "compact";
10
10
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,uCAAuC;AACvC,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,uDAAuD;AACvD,eAAO,MAAM,WAAW,eAAe,CAAC;AAExC,+CAA+C;AAC/C,eAAO,MAAM,WAAW,qBAAqB,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,uCAAuC;AACvC,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,uDAAuD;AACvD,eAAO,MAAM,WAAW,eAAe,CAAC;AAExC,6FAA6F;AAC7F,eAAO,MAAM,yBAAyB,YAAY,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 本地语义嵌入(recall-semantic 的向量引擎)。
3
+ *
4
+ * - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX,多语言,
5
+ * 中英 + 代码均可处理),模型文件在 models/ 下,完全离线、零下载。
6
+ * - 运行时下载:模型 onnx 缺失时由 ensureModelReady 后台下载(不阻塞、单飞、
7
+ * 失败自动重试),下载逻辑见 model-download.ts;就绪前工具告知模型。
8
+ * - 懒加载:首次调用才 `import('@huggingface/transformers')` 并加载 pipeline,
9
+ * 插件启动不阻塞;加载结果单例缓存(resetEmbedder 供测试重置)。
10
+ * - 批处理:一次推理多条文本(mean pooling + L2 归一化),输出每条的向量。
11
+ * - 相似度:cosineSimilarity 纯函数(归一化向量点积即余弦)。
12
+ *
13
+ * 依赖说明:@huggingface/transformers 为运行时依赖(v4,dtype q8 加载
14
+ * model_quantized.onnx),node 侧使用 onnxruntime-node 原生绑定。
15
+ */
16
+ import { EMBEDDING_MODEL_ID, type ModelFetch } from './model-download.ts';
17
+ /** 打包模型目录:<包根>/models/<model-id>/。 */
18
+ export declare const BUNDLED_MODEL_DIR: string;
19
+ export { EMBEDDING_MODEL_ID };
20
+ /** 嵌入函数类型:批量文本 → 每条一个向量(Float32Array)。 */
21
+ export type EmbedFn = (texts: readonly string[]) => Promise<Float32Array[]>;
22
+ /**
23
+ * 获取(或加载)嵌入函数。首次调用动态 import transformers 并加载本地模型;
24
+ * 之后复用同一 pipeline。失败时抛出可读错误(调用方自行降级)。
25
+ */
26
+ export declare function getEmbedder(modelDir?: string): Promise<EmbedFn>;
27
+ /** 重置模型单例(测试用:卸载已加载的 pipeline)。 */
28
+ export declare function resetEmbedder(): void;
29
+ /** 模型就绪状态:ready=本地已就绪可直接加载;downloading=缺失,后台下载中/将自动重试。 */
30
+ export type ModelStatus = 'ready' | 'downloading';
31
+ /**
32
+ * 确保模型就绪(运行时按需下载编排)。
33
+ * - 本地 onnx 已存在 → 'ready'(不触发下载)。
34
+ * - 缺失 → 启动后台下载(不阻塞,单飞)并返回 'downloading';下载失败仅调用
35
+ * warn 记录日志并结束本次尝试,下次调用会重新触发下载(自动重试)。
36
+ * - warn 可选:下载失败时的日志回调(默认静默;apply 注入 ctx.logger.warn)。
37
+ * - fetchImpl 可注入(测试传替身,默认全局 fetch)。
38
+ */
39
+ export declare function ensureModelReady(modelDir?: string, warn?: (message: string) => void, fetchImpl?: ModelFetch): Promise<ModelStatus>;
40
+ /** 重置下载状态(测试用:清空在途下载任务记录)。 */
41
+ export declare function resetModelDownloads(): void;
42
+ /**
43
+ * 两个向量的余弦相似度(向量已 L2 归一化时点积即余弦;这里兜底再归一化)。
44
+ * 任一向量为零向量返回 0。
45
+ */
46
+ export declare function cosineSimilarity(a: Float32Array | readonly number[], b: Float32Array | readonly number[]): number;
47
+ //# sourceMappingURL=embedding.d.ts.map
@@ -0,0 +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"}
package/dist/index.d.ts CHANGED
@@ -4,11 +4,15 @@
4
4
  *
5
5
  * 模块:
6
6
  * - recall.ts recall({ start_id, end_id?, offset? }) 工具:按 message_id 回看原始会话
7
+ * - semantic-recall.ts recall-semantic({ query, top_k?, start_id?, end_id?, offset? }) 工具:
8
+ * 按语义在全部消息日志(含被压缩/遮蔽)中检索,返回最匹配的完整消息与匹配说明
9
+ * (本地 ONNX embedding,模型随插件打包,懒加载)
7
10
  * - compress.ts 自动压缩(OM 观察/反思两级阈值):pre-step 阻塞串行执行——
8
- * 反思(摘要 ≥ 窗口 × historyMergeRatio fork 精简合并 <om-history>)、
9
- * 观察(未压缩消息 ≥ 窗口 × thresholdRatio 时 fork 压缩为观察日志并追加)
11
+ * 反思(摘要 ≥ 窗口 × historyMergeRatio 时摘要调用精简合并 <om-history>)、
12
+ * 观察(未压缩消息 ≥ 窗口 × thresholdRatio 时摘要调用压缩为观察日志并追加)
10
13
  *
11
- * 约束:不引入自定义会话事件类型——压缩复用宿主已知的 'compaction/prune' 影子价格事件。
14
+ * 约束:不引入自定义会话事件类型——压缩复用宿主已知的 compaction/* 生命周期事件
15
+ * (start/summary/end)与 checkpoint 标记,结果写入消息记录与轨迹。
12
16
  * 仅主会话生效(subagent 不压缩、recall 拒绝)。
13
17
  */
14
18
  import type { Context } from './types.ts';
@@ -17,7 +21,8 @@ export declare const name = "dsh-plugin-om";
17
21
  /** 插件注入的服务依赖(tools/llm/tokenMeter/sessions),由宿主按序注入。 */
18
22
  export declare const inject: string[];
19
23
  /**
20
- * 插件激活入口:注册 recall 工具,并在 agent/pre-step 阻塞触发两级自动压缩
24
+ * 插件激活入口:注册 recall / recall-semantic 工具(由配置键 recallEnabled /
25
+ * semanticRecallEnabled 控制),并在 agent/pre-step 阻塞触发两级自动压缩
21
26
  * (先反思后观察)。仅主会话生效。
22
27
  */
23
28
  export declare function apply(ctx: Context, config?: unknown): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,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;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAuB1D"}
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"}