## 需求 ACP 目前只有**事后压缩**能力(compress/decompress/prune),缺少**预防性截断**——在工具输出进入上下文之前就做动态截断。 ## 背景 OMO (omo-stable) 有一个 `tool-output-truncator` hook,在 `tool.execute.after` 时对特定工具的输出做动态截断: - 查询当前 session 的最后一条 assistant 消息的 token 用量(input + cache.read) - 计算剩余上下文空间 - 截断上限 = `min(剩余空间 × 0.5, 50K tokens)` — 上下文越满,截断越激进 - 保留前 3 行 header + 尽量多的内容行 - 对 grep/glob/lsp/ast_grep/interactive_bash/skill_mcp/webfetch 等工具生效 这是 ACP 缺失的能力。ACP 的 compress 是事后压缩(生成摘要),无法防止单次超大输出在进入上下文前就占满空间。 ## 建议实现 将 OMO 的 `dynamic-truncator` 逻辑迁入 ACP,作为 `tool.execute.after` hook: 1. 在工具输出进入上下文前,检查当前上下文使用率 2. 动态计算截断上限(基于剩余空间) 3. 截断保留 header + 尽量多的内容 4. 追加截断标记 源码参考:`omo-stable/src/shared/dynamic-truncator.ts`(193 行)+ `omo-stable/src/hooks/tool-output-truncator.ts`(61 行) ## 收益 - 填补 ACP 预防性截断的空白 - OMO 可以完全移除上下文管理组件,只保留编排能力 - 统一上下文管理到 ACP 一处
Activity
ranxianglei commentedon Sep 12, 2026
确认
是否属实
ranxianglei commentedon Sep 12, 2026
核实完毕(基于 origin/master,v1.17.0 = release commit df690fc):方向属实,但「已覆盖大半」言过其实。
属实的部分:
enforceContextBudget(新文件lib/messages/enforce-budget.ts,PR fix: context budget guard + no-window warning (#347) #350/[Bug] Silent context-overflow death on resume when model reports limit.context=0 (custom providers): uncalibrated thresholds → 400 → empty exit-0 loop #347)+truncateLargeToolOutputs(lib/messages/truncate-tools.ts,2026-07-28 bug-hunt 迭代创建,v1.17.0 的 fix: context-limit safety net for spawn+resume mode (#346) #349 更新了其阈值解析)。tool.execute.after钩子(唯一的 execute 钩子是command.execute.before),也没有任何相关 PR/issue 在推进本需求。Feature: Proactive Dynamic Output Truncation (tool-output-truncator) | Feature: 预防性动态输出截断 (tool-output-truncator) #101 维持 OPEN 是正确的。不准确的部分——「覆盖大半」高估了:
这两个机制都是事后/反应式的,运行在
experimental.chat.messages.transform管道里,作用对象是已经在上下文里的旧工具输出(截断或清空它们来腾空间);而 #101 的核心提案是预防式的——在tool.execute.after时把新输出在进入上下文之前就按剩余空间动态截断(cap = min(剩余×0.5, 50K)、保留 header 行、针对 grep/glob/lsp/bash/webfetch 等特定工具)。逐项对照:tool.execute.after钩子关键一点:单次超大输出在自己的那一轮仍会完整进入上下文——
enforce-budget.ts明确保护最近 3 条消息(PROTECT_RECENT_MESSAGES=3),它清的是旧输出而不是这条新的大输出。所以 issue 标题里的「预防性」这个核心缺口一个字节都没补上。结论:issue 应保持 open(这点说法正确),但措辞建议修正为——「v1.17.0 已提供反应式安全网层(truncate-tools + enforceContextBudget,防溢出/400 死循环);#101 提案的预防式机制(tool.execute.after 入上下文前动态截断)尚未开始实现,这才是 issue 的主体」。两者是互补关系,不是完成度关系。
ranxianglei commentedon Sep 14, 2026
现在是否可以做类似于omo的输出截断?超过一定大小截断即可。防止上下文膨胀
ranxianglei commentedon Sep 14, 2026
可以做,技术可行性已验证:
@opencode-ai/pluginSDK 确实暴露了"tool.execute.after"钩子(签名(input: {tool, sessionID, callID, args}, output: {title, output, metadata}) => Promise<void>),其中output.output是可变的字符串——正是 OMO 用的那个切入点,在输出持久化进会话前修改它即可。ACP 的index.ts已有统一的guard()钩子接线模式(#337 bili-proxy self-disable 也会自动覆盖新钩子),接入成本很低。按你说的「超过一定大小截断即可」,我的建议方案(简化静态版,不做 OMO 的动态 min(剩余×0.5, 50K)):
设计
lib/messages/truncate-on-execute.ts)+index.ts注册一个tool.execute.after钩子。核心逻辑无副作用、可单测。maxTokens(默认 20000 tokens,约 80K 字符;正常有用的输出基本 <5K tokens,这个默认值只拦真正的巨型输出)→ 截断为前 2000 字符 + 后 2000 字符 + 截断标记,与现有truncate-tools.ts的[truncated for context space...]标记同族(头部保留 header/命令回显,尾部保留错误/结论)。commands.protectedTools+compress.protectedTools语义,skill/compress/task/todowrite/write/edit 等绝不动(Bug 39 parity)。主会话和子代理会话都生效(这是保护机制,不是压缩管理)。{ "toolOutputTruncation": { "enabled": true, "maxTokens": 20000 } }truncateLargeToolOutputs/enforceContextBudget继续清「已积累的旧输出」(反应层)。标记幂等,反应层看到已截断内容会跳过。边界说明
enabled: false或调大maxTokens关闭/放宽。确认的话我就开工:分支
2026-09-14_tool-output-truncator+ devlog(REQ/WORKLOG)+ 实现 + 测试(代码与测试各过双 agent review)+ PR 引用 #101。如果这个简化版符合你的预期,PR 合并后 #101 即可关闭;若你还想要动态版,issue 维持 open 另开后续需求。[-]Feature: 预防性动态输出截断 (tool-output-truncator)[/-][+]Feature: Preventive Dynamic Output Truncation (tool-output-truncator) | Feature: 预防性动态输出截断 (tool-output-truncator)[/+][-]Feature: Preventive Dynamic Output Truncation (tool-output-truncator) | Feature: 预防性动态输出截断 (tool-output-truncator)[/-][+]Feature: Proactive Dynamic Output Truncation (tool-output-truncator) | Feature: 预防性动态输出截断 (tool-output-truncator)[/+]