d13d7c1714bdd1c2827dfcbf97502ed08636d8b1
compaction-cn — 让 OC 对话压缩摘要输出简体中文
OpenCode 原生压缩(compaction)提示词硬编码在 opencode.exe 二进制里,摘要默认纯英文;
后续任务里模型很容易被前面那段英文摘要带偏。本插件在压缩请求发出前往 system 追加
中文输出指令,让摘要内容用简体中文、标题原样保留英文(标题不能译,原因见下)。
分支名:compaction-cn | 文件:compaction-cn.ts
一、安装
源码真源 / 本机生效 / WSL 生效三份同步(MD5 必须一致):
源码真源 : D:\桌面\源码\oc_session\compaction-cn\compaction-cn.ts
本机生效 : C:\Users\ypshidifu\.config\opencode\plugins\compaction-cn.ts
WSL 生效 : /root/.config/opencode/plugins/compaction-cn.ts
插件在 session 初始化时加载(不是进程启动时),装好后新建一个会话即可生效, 已开会话不会热加载。
二、为什么只能这样干预(2026-09-25 实测定稿)
| 候选干预点 | 结论 |
|---|---|
V1 hooks(default 对象上的字段,含官方文档的 experimental.session.compacting) |
❌ 2.0.15 V2 运行时零消费者,探针 24 次 setup 全成功、A.* 日志全空;且 named export 返回 hooks 对象会被当 cleanup 调用 → TypeError: Ae is not a function 整批插件崩 |
opencode.config.compaction |
❌ schema 只有 {auto, buffer, keep.tokens},没有 prompt 覆盖字段 |
ctx.session.hook("model.request", cb) |
❌ payload 是 [sessionID, agent, model, kind, baseURL, headers],没有 messages,只能识别(kind="compaction")不能改写内容 |
ctx.session.hook("compaction", cb) |
✅ 唯一可行。payload = [sessionID, model, system, messages, options, agent, tools],system 会带进最终压缩请求 |
压缩请求时序(二进制逆向):
compaction hook(prep) → [...messages, 模板 Zt.user(z$(...))] → model.request(kind="compaction")
模板是在钩子之后才追加进 messages 的,所以钩子改不到模板本身,只能改 system。
关键约束:小节标题绝对不能翻译
输出校验 ww = (e) => e.split("\n").some(a => U$.includes(a.trim())),
U$ 是模板按行过滤出的英文标题行。摘要里必须整行命中这些英文标题,否则压缩直接失败:
## Objective / ## Requirements / ## Decisions / ## Work State /
### Completed / ### Active / ### Blocked /
## Next Move / ## Relevant Files / ## Important Context
所以本插件只加规则「内容中文、标题原样英文」,外加路径/代码/URL/报错原文不翻译。
三、工作方式
setup(ctx) 里向 ctx.session.hook("compaction", cb) 注册一个回调,往 g.system
数组 push 一段带 <compaction-language> 标记的文本。带标记是为了幂等判重——
多 location、多次 reload 可能重复注册钩子,已注入过就不再加。
日志(不进 stdout,避免污染 TUI):
C:\Users\ypshidifu\.config\opencode\compaction-cn.log # Windows
/root/.config/opencode/compaction-cn.log # WSL
正常会看到:
compaction 钩子注册完成 registration=[...]
已注入中文输出指令 sessionID="ses_xxx" system段=5 messages=N
四、V2 插件契约(与 ai-mcp 同款,勿破坏)
default导出 必须给{ id, setup }(缺了报Plugin must export a default definition...)setup必须返回 void,绝不抛异常(返回hooks/truthy 对象 →Ae is not a function整批崩)- named export 用逗号表达式安全形态:
export const X: any = async (_ctx: any) => (await ({} as any), undefined as any) - 本插件所有异常都在内部吞掉并落日志,绝不影响同批其它插件
五、效果对比
压缩前(原生英文输出):
## Objective
- Respond to the user with "收到".
装插件后(e2e 实测,has_en_headers=True、英文标题原样保留、正文全中文):
## Objective
- 按用户要求回复"收到"。
## Requirements
- 回复内容必须为"收到"。
...
Languages
JavaScript
57.9%
Python
35.9%
TypeScript
6.2%