Skip to content

feat: /compact 走 ACP 压缩管线 + i18n + usageTriggerPercent - #119

Open
21307369 wants to merge 13 commits into
ranxianglei:masterfrom
21307369:2026-08-12_omp-compat-i18n-kernel
Open

feat: /compact 走 ACP 压缩管线 + i18n + usageTriggerPercent#119
21307369 wants to merge 13 commits into
ranxianglei:masterfrom
21307369:2026-08-12_omp-compat-i18n-kernel

Conversation

@21307369

@21307369 21307369 commented Aug 11, 2026

Copy link
Copy Markdown

变更摘要

/compact 拦截 → ACP 压缩管线

  • wireCompactionDisable 从简单 cancel 改为完整 ACP 压缩流程:acquireLock → processTurn → 取最大可压缩 range → summarizeRange(压缩模型生成 summary)→ applyCompression → 返回 Pi compaction entry
  • 失败(无 model/无 range/kernel 拒绝)回退 Pi 默认压缩,永不返回 { cancel: true }(Pi 手动 /compact 会因此抛 "Compaction cancelled")
  • 新增 summarizeRange() 函数(src/auto-compress.ts),供 /compact handler 和自动压缩共用
  • session_before_compact handler 阻塞前 TUI notify "ACP 正在压缩上下文",避免用户误以为卡死
  • auto-compress 调用压缩模型前同样 notify(ctx.ui?.notify?.(t("compact.compressing", ...))

工具输出清洗(bash 去重去噪)

  • 新增 src/clean-output.ts 纯函数模块:折叠 bash 输出的无缩进重复行(≥2 次标 ×N)、去 \r 覆盖、折叠连续空行
  • 代码行豁免(缩进行、语法锚 {};)误伤率 0.24%
  • 通过 tool_result 钩子注入(src/tool-guardrails.ts),toolOutputClean?: boolean 配置,默认 true

新增 acp.json 配置项

  • language"zh" | "en",覆盖 LANG 检测;/acp-settings 新增语言选项,切换后立即生效无需重启
  • usageTriggerPercent:0-100,默认 25,0=禁用。上下文使用率到达该水位即注入压缩提示(独立于 kernel 的 growth 驱动)
  • toolOutputClean:bash 工具输出清洗开关,默认 true
  • /acp-settings 补齐 usageTriggerPercent(0-100 校验,0=禁用)与 toolOutputClean(开关)两项 UI

配置 UI 可测试化重构

  • 抽取纯函数 applySetting(id, value)src/commands.ts):patch 构建 + 校验逻辑(boolean/数字 0-100/语言)从 TUI 回调中解耦,无需 mock TUI 即可单测
  • fmt()nullundefined 同等处理(配置写 null 时显示默认值而非字面量)
  • 新增 5 个 settings 单测:范围校验、boolean 映射、数字拒绝、语言清除、未知 id

i18n 完善

  • nudge 附加文本(压缩块列表、最小字符数提示)支持中文化
  • settings 描述文案增加单位说明和示例值
  • compact handler 和 auto-compress 通知均有国际化支持

合并 master(delegate 追踪 + compress config)

  • Delegate usage 追踪(resetDelegateUsagesetDelegateDisplayUsageresolveDelegate)集成到 session 生命周期
  • Compress 配置合并:delegate/compress/displayUsage + usageTriggerPercent/languagesrc/user-config.ts
  • TUI footer status 状态显示(src/footer-status.ts
  • Watchdog / delegate-events 增强

修复

  • 更新 kernel 默认阈值测试至 0.0.21(maxContextLimitPct: 0.55emergencyThresholdPct: 0.8truncate.threshold: 1
  • 恢复 merge 冲突中被 master 旧逻辑覆盖的 /compact 拦截实现
  • config.ts 注释错位:usageTriggerPercent 文档混入了 toolBashDefaultTimeout 的注释块
  • declare module "acp-kernel" 扩展 Config 类型,透传自定义字段

新增/修改文件

  • 新增src/auto-compress.ts(301 行)、src/clean-output.ts(124 行)、src/footer-status.ts(51 行)、src/i18n.ts(143 行)、src/fleet-widget.ts(9 行)
  • 新增测试auto-compress.test.tsclean-output.test.tscommands.test.ts(settings)、i18n.test.tsfooter-status.test.tsdelegate-entry.test.ts
  • 修改src/index.ts(+171/-27)、src/commands.ts(+334/-33)、src/config.ts(+96/-5)、src/tool-guardrails.ts(+30/-9)、src/delegate-tool.ts(+249/-37)等
  • 文档CONFIGURATION.md / CONFIGURATION.zh-CN.md(237 行)

验证

  • tsc --noEmit 通过
  • npm test 287 通过(fail 0)
  • npm run build 通过(716.8 KB)

lsmir3 and others added 2 commits August 12, 2026 02:27
- /compact 拦截 Pi 内置 compaction,改走 ACP compress 管线(acquireLock → processTurn → summarizeRange → applyCompression → 返回 compaction entry)
- 失败(无 model/无 range/kernel 拒绝)回退 Pi 默认压缩,永不 cancel
- 新增 acp.json 配置项:language(zh/en,覆盖 LANG 检测)、usageTriggerPercent(0-100,默认 25,0=禁用)
- /acp-settings 新增语言选项,切换后立即生效无需重启
- i18n 完善:nudge 附加文本(压缩块列表、最小字符数提示)支持中文化
- 修复 config.ts 注释错位(usageTriggerPercent 文档与 timeout 注释混在一起)

Co-authored-by: Orca <help@stably.ai>
@21307369
21307369 force-pushed the 2026-08-12_omp-compat-i18n-kernel branch from 49a8623 to 487a18f Compare August 12, 2026 12:43
@21307369 21307369 changed the title feat: i18n commands, auto-compress default-model fallback, omp compat feat: /compact 走 ACP 压缩管线 + i18n + usageTriggerPercent Aug 12, 2026
@ranxianglei

Copy link
Copy Markdown
Owner

这里面内容比较多有一些涉及到重构方面.我需要拆分合并

lsmir3 added 3 commits August 13, 2026 18:57
- Pure function module clean-output.ts with O(n) dedupe using Map indexing
- Dedup globally repeated non-code lines (≥2 occurrences → ×N tag)
- Skip indented lines and code-anchored lines (syntax: {}, ;, ))
- Collapse \r progress-bar frames to final frame
- Collapse runs of ≥2 blank lines to one
- Wire into tool_result hook after cap, skip isError outputs
- Config: toolOutputClean (default true)
- 11 tests covering npm alternation, code immunity, CRLF, order
- Restore HEAD's session_before_compact interception (ACP compression for /compact)
  that was lost in the earlier conflict resolution, merged with master's
  delegate usage tracking (resolveDelegate, resetDelegateUsage)
- Restore user-config.ts merged version (usageTriggerPercent/language +
  delegate/compress/displayUsage)
- Update kernel threshold test to 0.0.21 defaults (0.55/0.8/1)
- 282 tests pass, typecheck + build clean
autoCompress blocks the main reply for up to 60s while the compression
model summarizes. Notify via ctx.ui before the blocking call so the
user knows ACP is working, not hung.
@21307369

Copy link
Copy Markdown
Author

你可能要重新弄了

lsmir3 and others added 5 commits August 13, 2026 20:01
Both config keys existed in config.ts but were missing from the
settings UI. usageTriggerPercent: 0-100, 0=disabled (default 25);
toolOutputClean: on/off boolean (default on).
- Extract TUI callback patch logic into exported applySetting(id, value)
  so validation (usageTriggerPercent 0-100, boolean/numeric/language
  branches) is unit-testable without a TUI mock
- fmt() now treats null like undefined (acp.json "usageTriggerPercent": null
  shows default 25 instead of literal "null")
- 5 new tests: range validation, boolean mapping, numeric rejection,
  language clear, unknown id
…onflicts)

Conflicts resolved in 5 files, HEAD-side wins where it supersedes:
- commands.ts: keep i18n imports + t() description, drop master dupes
- config.ts: keep declare module + full field set, drop base/master dupes
- index.ts: keep master's setDelegateDisplayUsage, restore indentation
- user-config.ts: keep full KNOWN set + interface fields
- tests/config.test.ts: HEAD version (0.0.21 thresholds + resolveDelegate)
  is a superset of master (0.0.20 thresholds) — took ours wholesale

Verification: typecheck pass, 287 tests (286 pass, 1 skipped), build pass.
- tests/config.test.ts: registry acp-kernel 0.0.21 defaults are
  0.75/0.95/0.95 (same as 0.0.20), not 0.55/0.8/1. The 0.55 values came
  from a locally overlaid unpublished master build (0.0.22 behavior);
  CI installs real 0.0.21 from npm.
- tests/commands.test.ts: withTempHome now also sets USERPROFILE —
  Windows os.homedir() reads USERPROFILE, not HOME, so saveConfig tests
  read the real user config instead of the temp dir.
- Local node_modules restored via npm ci (acp-kernel 0.0.21).
@ranxianglei

Copy link
Copy Markdown
Owner

我现在着手把这个合并进去 因为前面的部分重构已结束

@ranxianglei

Copy link
Copy Markdown
Owner

@21307369 感谢这个 PR。/compact 走 ACP 管线的方向是对的,但这个 PR 体量很大(3139 行)捆绑了不少东西,而且有几处设计需要调整。详细过审后建议拆成多个独立 PR,下面逐项说明。

1. acp_summary_ 不能改成内联 user 消息(需还原)

src/messages.ts 把内核的 acp_summary_ 占位消息改写成 role:"user" 给模型看。这个要拿掉,保留 master 的丢弃(if (core.id.startsWith("acp_summary_")) continue;)。原因:

  • compress 工具本身就是压缩动作。模型调 compress / 触发压缩时,block 已创建、被覆盖消息已剪掉——压缩已完成。内核留下的 acp_summary_ 只是占位标记,不是给模型再读一遍的摘要。
  • 不丢掉就重复:block 里已存了同一份摘要(供 decompress 按需取回),再当消息内联回去,等于压缩删了 N token 又塞回 M token,净收益变小,还和 block 机制重复。
  • role:"user" 是角色误用:模型会把摘要当成「用户在跟它说话」,可能被当成用户指令——这是最危险的点。

ACP 的设计是「激进剪枝 + 按需取回」:压缩后这段在上下文里就没了,模型需要细节时调 decompress。请还原成丢弃。

2. /compact → ACP 方向对,但要单独分支 + 改进

  • 拆到单独分支,配独立测试和回归。
  • 默认范围选择有问题:selectRangeSpan 的选段逻辑需要重新审视(选哪段、多大)。
  • 本质上是手动做一次压缩/蒸馏,却没有用内核对应层级的提示词。当前用的是一段手写的 SYSTEM_PROMPT,没用内核统一的压缩规则(T1 compress rules / T2 distillation rules)。这会导致压缩质量明显不如 compress 工具。应该接入 feat(prompts): customizable prompts via acp.json (Layer 2) #128 刚合并的 resolvePrompts() / Prompts,用内核统一的压缩规则来生成摘要。

3. usageTriggerPercent 建议去掉

内核 decideNudge 已有两套上下文触发器:

  • 增长触发:相对上次增长 ≥ max(5万 token, limit×5%) 就 nudge;
  • overLimit:usage ≥ 75%(maxContextLimitPct)直接 nudge;
  • 还有 emergency 更高阈值。

usageTriggerPercent(默认 25)虽加了 !shouldInject 守卫不会双重 nudge,但它是「比内核设计更早触发」的额外触发器——25% 就 nudge,远早于 75%,基本多余。建议去掉,依赖内核已有机制;若保留请大幅提高默认值。

4. declare module "acp-kernel" 去掉

config.ts 用 module augmentation 把 usageTriggerPercentlanguage 塞进内核 Config 类型。这是分层泄漏——UI 语言、nudge 阈值是适配层的事,不该进内核 Config。请留在 AdapterConfig 上读。

5. i18n 会影响模型注入的提示词(需改造)

查了调用点:i18n.tsnudge.compressedBlocks / nudge.minCharsnudgeMessage()(index.ts:407/412)被拼进 role:"user" 的 nudge 消息,push 进上下文(index.ts:325)直接发给模型

→ 模型看到的压缩指令会随 zh/en 变化。压缩是给模型的指令,措辞应保持稳定,否则不同语言下压缩行为/质量可能不一致。

建议:i18n 只保留给纯展示(UI notify / 命令描述 / settings label);所有注入模型的消息文本保持英文稳定。要做请单独分支。

6. 其它改动请拆成独立 PR

  • capToolOutput 重构(截断时重排内容顺序)——行为变更,单独 PR + 测试。
  • resolveToFilePath 符号链接加固(decompress-tool.ts)——好的安全修复,建议单独提小 PR,可优先合
  • pruneStaleRefs(state.ts)——防御性清理,单独 PR。

建议拆分

PR 内容 优先级
A resolveToFilePath 符号链接安全修复 高,可先合
B /compact → ACP(接 resolvePrompts + 修范围选择 + 独立测试)
C clean-output(bash 去重,纯 tool_result 展示)
D i18n 仅展示层(注入模型文本保持英文) 低,看受众
去掉 acp_summary_ 内联、usageTriggerPercent、module augmentation

整体方向认可,麻烦按上面拆分调整。有问题继续讨论 👍

@ranxianglei

Copy link
Copy Markdown
Owner

上面模型回复的,有不准确的地方,我来纠正

1. acp_summary_ 不能改成内联 user 消息(需还原)

补充,丢弃内核的消息,这是巧妙的地方 .为什么丢掉内核的总结 因为模型调用工具,compress 本身就是压缩本身.如果不丢弃就重复.这是opencode-acp的经验.

5. i18n 会影响模型注入的提示词(需改造)

现在支持自定义提示词.

ranxianglei pushed a commit that referenced this pull request Aug 13, 2026
Intercept Pi's session_before_compact: instead of cancelling native
compaction, pick a compressible span (selectRangeSpan), summarize it with a
model, and applyCompression, then hand the summary back to Pi as the
compaction result. On any failure (no compressible ranges, no usable model,
unparseable response, kernel rejection, exception) return undefined so Pi
falls back to its own compaction rather than losing context.

The summary prompt is built from the kernel's load-bearing compression rules
(compressPhilosophy + howToCompressRules) via the Prompts interface, so
/compact honors acp.json prompt overrides and stays consistent with the
compress tool — NOT a hand-rolled prompt that would drift from the rules.

Model resolution: explicit compressModel (provider:modelId in acp.json) wins,
else the current session model (zero-config). Adds @earendil-works/pi-ai as a
devDependency for the complete() type (kept external at runtime via tsup).

Extracted from #119 (lsmir2).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants