billion-context-omp 0.1.0 → 0.1.2

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,61 +1,199 @@
1
+ # billion-context-omp
2
+
1
3
  [English](./README.md) | [中文](./README.zh-CN.md)
2
4
 
3
- # billion-context-omp
5
+ <p align="center">
6
+ <strong>Billion-Context</strong> for <a href="https://github.com/can1357/oh-my-pi">omp (oh-my-pi)</a>
7
+ <br />
8
+ The model decides <em>when</em> and <em>what</em> to compress — not a hard limit.
9
+ </p>
10
+
11
+ ---
12
+
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/billion-context-omp"><img src="https://img.shields.io/npm/v/billion-context-omp.svg?style=flat-square" alt="npm"></a>
15
+ <a href="https://github.com/ranxianglei/billion-context-omp/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/billion-context-omp.svg?style=flat-square" alt="license"></a>
16
+ <a href="https://github.com/ranxianglei/billion-context-omp"><img src="https://img.shields.io/badge/GitHub-ranxianglei%2Fbillion--context--omp-181717?style=flat-square&logo=github" alt="GitHub"></a>
17
+ </p>
4
18
 
5
- [oh-my-pi (omp)](https://github.com/can1357/oh-my-pi) client extension for [billion-context](https://www.npmjs.com/package/billion-context).
19
+ <p align="center">
20
+ <code>omp install billion-context-omp</code>
21
+ </p>
6
22
 
7
- `billion-context` is a Node.js proxy that sits between any AI agent and its model API, rewriting Anthropic/OpenAI streams with [acp-kernel](https://github.com/ranxianglei/acp-kernel) compression. `billion-context-omp` wires **omp** — the terminal coding agent — into that pipeline: it builds the `base_url` override that routes omp's traffic through a running `billion-context` proxy, and **self-disables when it detects omp is already behind bili** so two layers of compression never stack.
23
+ ---
8
24
 
9
- > ⚠️ This is a **skeleton** package. The config-building helpers are placeholders.
10
- > Wire them to omp's actual provider/`base_url` config shape as you build it out.
25
+ ## Why?
11
26
 
12
- ## Why
27
+ When conversations get long, the model runs out of context. Most tools hard-truncate — silently dropping earlier messages. **billion-context** gives the model a `compress` tool: the LLM decides **when** and **what** to compress into high-fidelity summaries, preserving critical details (file paths, decisions, error strings) while reclaiming context space.
13
28
 
14
- Long coding sessions blow up context. Once you pass the context window the session degrades or dies, and every provider charges per token. Compression lets a single session run for days — billions of tokens through one window.
29
+ Unlike omp's built-in auto-compaction (which replaces everything with a single summary), billion-context:
30
+ - **Preserves structure** — compressed ranges become labeled blocks you can decompress later
31
+ - **Multi-tier** — summaries can be further distilled (T1 → T2 → T3) as sessions grow
32
+ - **Searchable** — `search_context` finds information inside compressed blocks without decompressing
33
+ - **Selective** — protected tools, user messages, and the recent working set are never compressed
15
34
 
16
- omp already supports arbitrary providers and custom `base_url`. This package is the thin glue that points those `base_url`s at a bili proxy and keeps the `/bili/` self-detection signal consistent with the rest of the billion-context client family (`billion-context-pi`, `opencode-acp`, …).
35
+ This means:
36
+
37
+ 1. **A single session handles enormous workloads.** Per simulation tests of the three-tier architecture (see [opencode-acp](https://github.com/ranxianglei/opencode-acp)), one session can process on the order of 10–60 billion cumulative tokens — while retaining long-term memory of distant key information (paths, decisions, signatures). You can work in the **same session for months** without outgrowing the context.
38
+ 2. **Context stays lean over the long run.** In practice context typically holds under ~150K tokens (opencode-acp keeps it under ~200K), so compared to traditional compaction that lets context balloon toward 1M, **a single session costs roughly 5× less in tokens**.
17
39
 
18
40
  ## Install
19
41
 
20
42
  ```bash
21
- npm install billion-context-omp
43
+ omp install billion-context-omp
22
44
  ```
23
45
 
24
- ## Quickstart
46
+ That's it. The extension auto-loads on next omp startup. No configuration needed — it reads your model's context window automatically.
47
+
48
+ Or add it to your omp settings (`~/.omp/agent/settings.json` or project `.omp/settings.json`):
25
49
 
26
- ```ts
27
- import { BillionContextOmp } from 'billion-context-omp';
50
+ ```jsonc
51
+ {
52
+ "extensions": ["billion-context-omp"]
53
+ }
54
+ ```
28
55
 
29
- const omp = new BillionContextOmp({ endpoint: 'http://localhost:8787' });
56
+ ## How it works
30
57
 
31
- // Route a provider through bili:
32
- omp.buildBaseUrl('https://api.openai.com/v1');
33
- // => 'http://localhost:8787/bili/https://api.openai.com/v1'
58
+ billion-context intercepts omp's `context` event (fired before each LLM call) and runs the acp-kernel pipeline:
34
59
 
35
- // Detect an already-routed URL (use to self-disable / avoid double compression):
36
- omp.isBiliBaseUrl('http://localhost:8787/bili/https://api.openai.com/v1'); // => true
37
60
  ```
61
+ assign refs → fold in-stream compress calls → prune → nudge → emergency truncate
62
+ ```
63
+
64
+ Each message gets an invisible `<acp>` ref tag (`m00001`, `m00002`, ...) visible to the model but not the user. The model uses these refs to specify compression ranges.
38
65
 
39
- ## API
66
+ **The session stream is the single source of truth.** Compress calls live in the stream itself: every compress tool call's arguments (ranges + summaries) are re-applied deterministically on each LLM call, on restart, and on resume — no sidecar state file to drift out of sync. Position ids (`p1..pN`) and model-facing refs (`m00001..`) are re-derived from the stream every turn; prefix rewrites (retry, rewind, host compaction) are detected and safely re-folded, with fingerprint guards against replaying a call onto the wrong messages.
40
67
 
41
- ### `new BillionContextOmp(options?)`
68
+ omp's built-in `/compact` is intercepted and replaced by an ACP model-summarized compaction that also preserves prior compress-call summaries — nothing is lost to the gap between the summary and the kept entries.
69
+
70
+ ## Plugin compatibility
71
+
72
+ **Keep exactly one context-compression plugin installed.** If two compression extensions both rewrite the message list, they clobber each other's work — compressed ranges can be re-expanded or corrupted. omp's own `/compact` is already intercepted automatically by billion-context-omp, but any *third-party* compression/compaction extension should be uninstalled.
73
+
74
+ ## Model-facing tools
75
+
76
+ | Tool | What it does |
77
+ |------|-------------|
78
+ | `compress` | Replace a contiguous message range with a detailed summary |
79
+ | `decompress` | Restore a previously compressed block's content (to file by default; `inline:true` for single messages) |
80
+ | `search_context` | Search compressed block summaries (and visible messages) by keyword |
81
+ | `acp_status` | Show context usage, compressed blocks, compressible ranges |
82
+
83
+ > The `acp_delegate` sub-agent subsystem from the Pi build is intentionally **not** registered — omp ships its own multi-agent orchestration, and duplicate delegation tools would conflict.
84
+
85
+ ## `/acp` command
86
+
87
+ Rich status display for the user:
88
+
89
+ ```
90
+ ╭─────────────────────────────────────────────╮
91
+ │ ACP Context Analysis │
92
+ ╰─────────────────────────────────────────────╯
93
+ billion-context-omp@0.1.1
94
+
95
+ Context: 6% (57k / 1.0M)
96
+
97
+ Token Breakdown:
98
+ SysPrompt ██░░░░░░░░░░░░░░░░░░ 10% 5.9k
99
+ Framework ██████████████████░░ 90% 51k
100
+
101
+ Nudge: idle — max compressible 0 < threshold 20000; growth 0 < floor 20000
102
+
103
+ Blocks: 2 active / 2 total (21k tokens compressed)
104
+ [b1] T1 5.1k→1.0k: Fold architecture port
105
+ [b2] T1 15k→799: Replay guard hardening
106
+
107
+ Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.
108
+ ```
109
+
110
+ ## Configuration
111
+
112
+ billion-context-omp works out of the box with no configuration. Optional keys can be set in a JSON config file.
113
+
114
+ ### Config file
115
+
116
+ Create `~/.omp/acp-omp.json` (global) and/or `<project>/.omp/acp-omp.json` (project-local, overrides global):
117
+
118
+ ```json
119
+ {
120
+ "debug": false,
121
+ "autoUpdate": true,
122
+ "modelContextLimit": 200000,
123
+ "compressModel": "zhipuai:glm-5.2",
124
+ "toolBashDefaultTimeout": 60,
125
+ "toolOutputMaxBytes": 200000,
126
+ "compress": {
127
+ "maxContextLimit": "75%",
128
+ "emergencyThresholdPercent": "95%",
129
+ "nudgeGrowthTokens": 50000
130
+ },
131
+
132
+ "prompts": {
133
+ "compressPhilosophy": "Override the compression philosophy...",
134
+ "howToCompressRules": "Override tier-1 rules...",
135
+ "tier2DistillRules": "Override tier-2 distillation rules...",
136
+ "tier3CondenseRules": "Override tier-3 condensation rules..."
137
+ },
138
+ "acknowledgePromptsRisk": true
139
+ }
140
+ ```
141
+
142
+ | Key | Default | Description |
143
+ |-----|---------|-------------|
144
+ | `debug` | `false` | Enable verbose **debug-level** events in the log. The always-on log (lifecycle events, errors, warnings) is written regardless; `debug` only adds extra diagnostics. Also enabled by env `ACP_DEBUG=1`. |
145
+ | `autoUpdate` | `true` | On session start (throttled to one check per 3 minutes), check npm for a newer version and auto-install it. Disable to avoid all startup network calls. |
146
+ | `modelContextLimit` | *(auto)* | Override the context limit (in tokens). Defaults to the model's `contextWindow`. |
147
+ | `compressModel` | *(session model)* | `provider:modelId` used for `/compact` model-summarized compaction (e.g. `"zhipuai:glm-5.2"`). Defaults to the current session model when omitted. |
148
+ | `toolBashDefaultTimeout` | `60` | Seconds injected into the `bash` tool when the model omits `timeout`. Without this a forgotten timeout can hang for thousands of seconds. `0` restores unbounded behavior. |
149
+ | `toolOutputMaxBytes` | `200000` | Hard byte cap on tool result text (applied via the `tool_result` hook). Stops runaway output that omp's own caps can't catch. When it fires the model is told where the full output lives; set lower (e.g. `8192`) for a tighter context budget, or `0` to disable. |
150
+ | `compress.maxContextLimit` | `"75%"` | Context usage threshold that triggers **forced compression** nudges (bypasses growth-gate + cadence). Accepts a ratio (`0.75`) or percent string (`"75%"`). Lower = compress earlier / more aggressively. |
151
+ | `compress.emergencyThresholdPercent` | `"95%"` | Context usage threshold that triggers **emergency truncation** of large tool outputs to keep the session alive. Must be ≥ `maxContextLimit`. |
152
+ | `compress.nudgeGrowthTokens` | `50000` | Token growth step for soft compression nudges. A nudge fires roughly every time this many tokens become compressible; if the model ignores it, it re-fires after the same amount of further growth. Lower = compress more often. |
153
+ | `prompts` | *(kernel defaults)* | Override acp-kernel's 4 load-bearing compression prompt rules (`compressPhilosophy`, `howToCompressRules`, `tier2DistillRules`, `tier3CondenseRules`). Each set field replaces the default verbatim; omitted fields are inherited. Requires `acknowledgePromptsRisk: true`. |
154
+ | `acknowledgePromptsRisk` | `false` | Safety gate for `prompts` overrides. Set `true` to acknowledge that replacing the tuned compression rules may reduce summary quality, and to make overrides take effect. |
155
+
156
+ The three nudge thresholds (`maxContextLimit`, `emergencyThresholdPercent`, `nudgeGrowthTokens`) form a three-tier escalation: growth-driven soft nudges → forced nudges at `maxContextLimit` → emergency truncation at `emergencyThresholdPercent`.
157
+
158
+ ### Environment variables
159
+
160
+ | Variable | Effect |
161
+ |----------|--------|
162
+ | `ACP_AUTO_UPDATE` | Set to `0` / `false` / `no` / `off` (case-insensitive) to disable auto-update, overriding the config. |
163
+ | `ACP_MODEL_CONTEXT_LIMIT` | Override the context limit. Takes precedence over the config value. |
164
+ | `ACP_DEBUG` | Set to `1` or `true` to enable debug-level logging (always-on events are written regardless). |
165
+ | `ACP_LOG_FILE` | Override the log file path (default `~/.omp/acp-omp.log`). |
166
+
167
+ ### Logging
168
+
169
+ billion-context-omp writes a structured, always-on log to `~/.omp/acp-omp.log` (override with `ACP_LOG_FILE`). It covers the model's whole working session and is useful for diagnosing problems:
170
+
171
+ - **Always written** (even with `debug: false`): `error`, `warn`, `info` levels — session start, every context turn (token usage / nudge decision), compress/decompress, and **all errors and warnings**. Error lines include the message and stack trace.
172
+ - **Written only when `debug: true`**: verbose `debug`-level diagnostics (full field dumps, per-turn internals, fold/replay events).
173
+
174
+ Each line: `<ISO timestamp> [<level>] [<scope>] key=value key=value`. The file rotates to `~/.omp/acp-omp.log.old` at 10 MB.
175
+
176
+ ```sh
177
+ tail -f ~/.omp/acp-omp.log # watch the session live
178
+ grep '\[error\]' ~/.omp/acp-omp.log # surface every recorded failure
179
+ ```
42
180
 
43
- | option | type | description |
44
- | ---------- | -------- | ---------------------------------------- |
45
- | `endpoint` | `string` | Origin of a running billion-context proxy. |
181
+ ### Compression philosophy
46
182
 
47
- ### `omp.buildBaseUrl(upstream): string`
183
+ The model receives detailed guidance (in its system prompt) on **when** to compress, **what** to keep verbatim (paths, signatures, errors, decisions, user intent), and **what** to drop (verbose logs, duplicates, consumed exploration). This guidance is injected on every turn so it stays in the model's attention.
48
184
 
49
- Wrap an upstream `base_url` as `${endpoint}/bili/${upstream}`. Throws if no endpoint is configured. Passes through unchanged if already routed.
185
+ ### What gets protected
50
186
 
51
- ### `omp.isBiliBaseUrl(baseUrl): boolean`
187
+ billion-context protects three categories of content from compression:
52
188
 
53
- True when the URL already carries the `/bili/` prefix use this to self-disable when omp's `base_url` is already pointing at bili.
189
+ 1. **Always-protected tools** `compress` calls are hard-protected (they're load-bearing metadata; compressing them breaks decompress and the "summary is historical" contract).
190
+ 2. **Soft recent-zone** — the last N messages (default 5) and last ~5K tokens are soft-protected so the model keeps its working set. Tool results from `decompress`, `search_context`, `read`, and `bash` are **excluded** from this zone: they're large and meant to be compressible once consumed.
191
+ 3. **Last user message** — always protected (user intent must survive).
54
192
 
55
- ### `omp.buildConfig(providers): Record<provider, { base_url }>`
193
+ ## Built on acp-kernel
56
194
 
57
- Build base_url overrides for multiple omp providers at once. _(Skeleton.)_
195
+ The compression engine is [`acp-kernel`](https://github.com/ranxianglei/acp-kernel) a platform-agnostic, MIT-licensed library. It's bundled inline into `dist/index.js`, so there are zero runtime dependencies.
58
196
 
59
197
  ## License
60
198
 
61
- MIT © [ranxianglei](https://github.com/ranxianglei)
199
+ MIT © ranxianglei
package/README.zh-CN.md CHANGED
@@ -2,60 +2,198 @@
2
2
 
3
3
  # billion-context-omp
4
4
 
5
- [oh-my-pi (omp)](https://github.com/can1357/oh-my-pi) 的 [billion-context](https://www.npmjs.com/package/billion-context) 客户端扩展。
5
+ <p align="center">
6
+ 面向 <a href="https://github.com/can1357/oh-my-pi">omp (oh-my-pi)</a> 的 <strong>Billion-Context</strong>
7
+ <br />
8
+ 由模型决定<em>何时</em>压缩、<em>压缩什么</em> —— 而不是硬截断。
9
+ </p>
6
10
 
7
- `billion-context` 是一个 Node.js 代理,架在任意 AI 助手与其模型 API 之间,用 [acp-kernel](https://github.com/ranxianglei/acp-kernel) 压缩重写 Anthropic/OpenAI 流。`billion-context-omp` 把 **omp** —— 终端编程助手 —— 接入这条链路:它生成 `base_url` 覆盖,把 omp 的流量路由到运行中的 `billion-context` 代理,并在**检测到 omp 已经位于 bili 之后时自动停用**,避免两层压缩叠加。
11
+ ---
8
12
 
9
- > ⚠️ 这是一个**骨架**包,配置构建辅助函数目前是占位实现。
10
- > 请按 omp 实际的 provider / `base_url` 配置形态对接后再行扩展。
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/billion-context-omp"><img src="https://img.shields.io/npm/v/billion-context-omp.svg?style=flat-square" alt="npm"></a>
15
+ <a href="https://github.com/ranxianglei/billion-context-omp/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/billion-context-omp.svg?style=flat-square" alt="license"></a>
16
+ <a href="https://github.com/ranxianglei/billion-context-omp"><img src="https://img.shields.io/badge/GitHub-ranxianglei%2Fbillion--context--omp-181717?style=flat-square&logo=github" alt="GitHub"></a>
17
+ </p>
11
18
 
12
- ## 为什么
19
+ <p align="center">
20
+ <code>omp install billion-context-omp</code>
21
+ </p>
13
22
 
14
- 长编程会话会把上下文撑爆。一旦超过上下文窗口,会话质量下降甚至崩掉,而各家 provider 按 token 计费。压缩能让**一个会话连跑数天** —— 海量 token 穿过同一个窗口。
23
+ ---
15
24
 
16
- omp 本就支持任意 provider 和自定义 `base_url`。本包提供薄薄一层胶水,把这些 `base_url` 指向 bili 代理,并保持 `/bili/` 自检信号与 billion-context 客户端家族(`billion-context-pi`、`opencode-acp` 等)一致。
25
+ ## 为什么?
26
+
27
+ 会话变长后,模型的上下文会耗尽。多数工具采用硬截断——悄悄丢弃早期消息。**billion-context** 给模型一个 `compress` 工具:由 LLM 自己决定**何时**压缩、**压缩什么**,生成高保真摘要,保留关键细节(文件路径、决策、报错原文)的同时回收上下文空间。
28
+
29
+ 与 omp 内置的自动 compaction(把一切都换成一条摘要)不同,billion-context:
30
+ - **保留结构** —— 被压缩的区间变成带标签的块,之后可以解压还原
31
+ - **多层蒸馏** —— 摘要可随会话增长继续蒸馏(T1 → T2 → T3)
32
+ - **可搜索** —— `search_context` 不解压即可在压缩块摘要中检索信息
33
+ - **有选择性** —— 受保护工具、用户消息、近期工作集永不被压缩
34
+
35
+ 这意味着:
36
+
37
+ 1. **单个会话可以承载巨大的工作量。** 三层架构的模拟测试(见 [opencode-acp](https://github.com/ranxianglei/opencode-acp))显示,一个会话可处理 100~600 亿累计 token——同时保持对早期关键信息(路径、决策、签名)的长期记忆。你可以在**同一个会话里工作数月**而不会撑爆上下文。
38
+ 2. **上下文长期保持精瘦。** 实际运行中上下文通常保持在 ~150K token 以下(opencode-acp 控制在 ~200K 以下)。相比放任上下文膨胀到 1M 的传统 compaction,**单个会话的 token 成本约低 5 倍**。
17
39
 
18
40
  ## 安装
19
41
 
20
42
  ```bash
21
- npm install billion-context-omp
43
+ omp install billion-context-omp
22
44
  ```
23
45
 
24
- ## 快速开始
46
+ 就这一步。扩展在下次 omp 启动时自动加载,无需任何配置——它自动读取模型的上下文窗口。
25
47
 
26
- ```ts
27
- import { BillionContextOmp } from 'billion-context-omp';
48
+ 或加入 omp 设置(`~/.omp/agent/settings.json` 或项目级 `.omp/settings.json`):
28
49
 
29
- const omp = new BillionContextOmp({ endpoint: 'http://localhost:8787' });
50
+ ```jsonc
51
+ {
52
+ "extensions": ["billion-context-omp"]
53
+ }
54
+ ```
30
55
 
31
- // 让某个 provider 走 bili:
32
- omp.buildBaseUrl('https://api.openai.com/v1');
33
- // => 'http://localhost:8787/bili/https://api.openai.com/v1'
56
+ ## 工作原理
34
57
 
35
- // 检测已路由的 URL(用于自我停用 / 避免双重压缩):
36
- omp.isBiliBaseUrl('http://localhost:8787/bili/https://api.openai.com/v1'); // => true
58
+ billion-context 拦截 omp `context` 事件(每次 LLM 调用前触发)并运行 acp-kernel 管线:
59
+
60
+ ```
61
+ 分配 ref → 折叠流内 compress 调用 → 剪枝 → 提醒 → 紧急截断
37
62
  ```
38
63
 
39
- ## API
64
+ 每条消息获得一个模型可见、用户不可见的 `<acp>` ref 标签(`m00001`、`m00002`、...)。模型用这些 ref 指定压缩区间。
65
+
66
+ **会话流是唯一真相源。** compress 调用本身就存在于流中:每次 compress 工具调用的参数(区间 + 摘要)在每次 LLM 调用、重启、resume 时被确定性重放——没有会漂移失步的独立状态文件。位置 id(`p1..pN`)和面向模型的 ref(`m00001..`)每轮从流重新推导;前缀改写(retry、rewind、宿主 compaction)会被检测并安全重折叠,指纹守卫防止把调用重放到错误的消息上。
67
+
68
+ omp 内置的 `/compact` 被拦截,替换为 ACP 模型摘要式 compaction,它同时保留之前的 compress 调用摘要——摘要与保留条目之间的空隙不会丢任何东西。
69
+
70
+ ## 插件兼容性
71
+
72
+ **只安装一个上下文压缩插件。** 若两个压缩扩展都改写消息列表,它们会互相破坏对方的工作——被压缩的区间可能被重新展开或损坏。omp 自己的 `/compact` 已被 billion-context-omp 自动拦截,但任何*第三方*压缩/compaction 扩展都应卸载。
73
+
74
+ ## 面向模型的工具
75
+
76
+ | 工具 | 作用 |
77
+ |------|------|
78
+ | `compress` | 把一段连续消息区间替换为详细摘要 |
79
+ | `decompress` | 恢复之前压缩的块内容(默认写文件;单条消息可 `inline:true`) |
80
+ | `search_context` | 按关键字搜索压缩块摘要(及可见消息) |
81
+ | `acp_status` | 显示上下文用量、压缩块、可压缩区间 |
82
+
83
+ > Pi 版中的 `acp_delegate` 子代理系统在此**有意不注册**——omp 自带多代理编排,重复的委派工具会冲突。
84
+
85
+ ## `/acp` 命令
86
+
87
+ 面向用户的富状态面板:
88
+
89
+ ```
90
+ ╭─────────────────────────────────────────────╮
91
+ │ ACP Context Analysis │
92
+ ╰─────────────────────────────────────────────╯
93
+ billion-context-omp@0.1.1
40
94
 
41
- ### `new BillionContextOmp(options?)`
95
+ Context: 6% (57k / 1.0M)
96
+
97
+ Token Breakdown:
98
+ SysPrompt ██░░░░░░░░░░░░░░░░░░ 10% 5.9k
99
+ Framework ██████████████████░░ 90% 51k
100
+
101
+ Nudge: idle — max compressible 0 < threshold 20000; growth 0 < floor 20000
102
+
103
+ Blocks: 2 active / 2 total (21k tokens compressed)
104
+ [b1] T1 5.1k→1.0k: Fold architecture port
105
+ [b2] T1 15k→799: Replay guard hardening
106
+
107
+ Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.
108
+ ```
109
+
110
+ ## 配置
111
+
112
+ billion-context-omp 开箱即用,无需配置。可选键写入 JSON 配置文件。
113
+
114
+ ### 配置文件
115
+
116
+ 创建 `~/.omp/acp-omp.json`(全局)和/或 `<项目>/.omp/acp-omp.json`(项目级覆盖全局):
117
+
118
+ ```json
119
+ {
120
+ "debug": false,
121
+ "autoUpdate": true,
122
+ "modelContextLimit": 200000,
123
+ "compressModel": "zhipuai:glm-5.2",
124
+ "toolBashDefaultTimeout": 60,
125
+ "toolOutputMaxBytes": 200000,
126
+ "compress": {
127
+ "maxContextLimit": "75%",
128
+ "emergencyThresholdPercent": "95%",
129
+ "nudgeGrowthTokens": 50000
130
+ },
131
+
132
+ "prompts": {
133
+ "compressPhilosophy": "覆盖压缩哲学...",
134
+ "howToCompressRules": "覆盖 tier-1 规则...",
135
+ "tier2DistillRules": "覆盖 tier-2 蒸馏规则...",
136
+ "tier3CondenseRules": "覆盖 tier-3 凝缩规则..."
137
+ },
138
+ "acknowledgePromptsRisk": true
139
+ }
140
+ ```
141
+
142
+ | 键 | 默认 | 说明 |
143
+ |-----|------|------|
144
+ | `debug` | `false` | 开启**debug 级**详细日志事件。常开日志(生命周期事件、错误、警告)无论如何都会写;`debug` 只增加诊断信息。也可用环境变量 `ACP_DEBUG=1` 开启。 |
145
+ | `autoUpdate` | `true` | 会话启动时(节流为每 3 分钟最多一次)检查 npm 是否有新版本并自动安装。关闭可避免所有启动期网络请求。 |
146
+ | `modelContextLimit` | *(自动)* | 覆盖上下文上限(token 数)。默认取模型的 `contextWindow`。 |
147
+ | `compressModel` | *(会话模型)* | `/compact` 模型摘要式压缩使用的 `provider:modelId`(如 `"zhipuai:glm-5.2"`)。缺省用当前会话模型。 |
148
+ | `toolBashDefaultTimeout` | `60` | 模型省略 `timeout` 时注入 `bash` 工具的秒数。没有它,一次忘记的 timeout 可能挂起数千秒。`0` 恢复无限制。 |
149
+ | `toolOutputMaxBytes` | `200000` | 工具结果文本的硬字节上限(经 `tool_result` 钩子实施)。拦截 omp 自身上限管不住的失控输出。触发时模型会被告知完整输出在哪;调低(如 `8192`)可更省上下文,`0` 禁用。 |
150
+ | `compress.maxContextLimit` | `"75%"` | 触发**强制压缩**提醒的上下文用量阈值(绕过增长门控与节拍)。接受比例(`0.75`)或百分比字符串(`"75%"`)。越低 = 越早/越激进压缩。 |
151
+ | `compress.emergencyThresholdPercent` | `"95%"` | 触发**紧急截断**(截断失控工具输出以保住会话)的上下文用量阈值。必须 ≥ `maxContextLimit`。 |
152
+ | `compress.nudgeGrowthTokens` | `50000` | 软压缩提醒的 token 增长步长。每积累约这么多可压缩 token 就提醒一次;模型无视则再增长同等数量后重新提醒。越低 = 越常压缩。 |
153
+ | `prompts` | *(kernel 默认)* | 覆盖 acp-kernel 的 4 条承重压缩提示规则(`compressPhilosophy`、`howToCompressRules`、`tier2DistillRules`、`tier3CondenseRules`)。每个设置的字段逐字替换默认值;未设置的字段继承默认。需要 `acknowledgePromptsRisk: true`。 |
154
+ | `acknowledgePromptsRisk` | `false` | `prompts` 覆盖的安全门。设 `true` 表示知悉替换调优过的压缩规则可能降低摘要质量,并使覆盖生效。 |
155
+
156
+ 三个提醒阈值(`maxContextLimit`、`emergencyThresholdPercent`、`nudgeGrowthTokens`)构成三级升级:增长驱动的软提醒 → `maxContextLimit` 处的强制提醒 → `emergencyThresholdPercent` 处的紧急截断。
157
+
158
+ ### 环境变量
159
+
160
+ | 变量 | 效果 |
161
+ |------|------|
162
+ | `ACP_AUTO_UPDATE` | 设为 `0` / `false` / `no` / `off`(不区分大小写)禁用自动更新,覆盖配置。 |
163
+ | `ACP_MODEL_CONTEXT_LIMIT` | 覆盖上下文上限。优先于配置值。 |
164
+ | `ACP_DEBUG` | 设为 `1` 或 `true` 开启 debug 级日志(常开事件无论如何都写)。 |
165
+ | `ACP_LOG_FILE` | 覆盖日志文件路径(默认 `~/.omp/acp-omp.log`)。 |
166
+
167
+ ### 日志
168
+
169
+ billion-context-omp 向 `~/.omp/acp-omp.log`(可用 `ACP_LOG_FILE` 覆盖)写入结构化的常开日志,覆盖模型整个工作会话,适合诊断问题:
170
+
171
+ - **始终写入**(即使 `debug: false`):`error`、`warn`、`info` 级——会话启动、每个 context 轮次(token 用量/提醒决策)、压缩/解压,以及**全部错误和警告**。错误行含消息与堆栈。
172
+ - **仅在 `debug: true` 时写入**:冗长的 `debug` 级诊断(完整字段转储、每轮内部状态、折叠/重放事件)。
173
+
174
+ 每行格式:`<ISO 时间戳> [<级别>] [<作用域>] key=value key=value`。文件在 10 MB 时轮转为 `~/.omp/acp-omp.log.old`。
175
+
176
+ ```sh
177
+ tail -f ~/.omp/acp-omp.log # 实时观察会话
178
+ grep '\[error\]' ~/.omp/acp-omp.log # 列出所有已记录的失败
179
+ ```
42
180
 
43
- | 选项 | 类型 | 说明 |
44
- | ---------- | -------- | --------------------------------- |
45
- | `endpoint` | `string` | 运行中的 billion-context 代理地址。 |
181
+ ### 压缩哲学
46
182
 
47
- ### `omp.buildBaseUrl(upstream): string`
183
+ 模型(在其系统提示中)收到详细指引:**何时**压缩、**什么**必须逐字保留(路径、签名、报错、决策、用户意图)、**什么**该丢弃(冗长日志、重复内容、已消费的探索)。该指引每轮注入,保持在模型注意力内。
48
184
 
49
- 把上游 `base_url` 包成 `${endpoint}/bili/${upstream}`。未配置 endpoint 时抛错。若已是路由过的 URL 则原样返回。
185
+ ### 受保护的内容
50
186
 
51
- ### `omp.isBiliBaseUrl(baseUrl): boolean`
187
+ billion-context 保护三类内容不被压缩:
52
188
 
53
- URL `/bili/` 前缀时返回 true —— omp 的 `base_url` 已指向 bili 时用此方法自我停用。
189
+ 1. **永久保护的工具** —— `compress` 调用被硬保护(它们是承重元数据;压缩它们会破坏 decompress 与"摘要属于历史"的契约)。
190
+ 2. **软性近期区** —— 最后 N 条消息(默认 5 条)与最后约 5K token 被软保护,模型保持工作集。`decompress`、`search_context`、`read`、`bash` 的工具结果**排除**在该区之外:它们体量大、消费后本就该可压缩。
191
+ 3. **最后一条用户消息** —— 永久保护(用户意图必须存活)。
54
192
 
55
- ### `omp.buildConfig(providers): Record<provider, { base_url }>`
193
+ ## 基于 acp-kernel
56
194
 
57
- 一次性为多个 omp provider 生成 base_url 覆盖。_(骨架。)_
195
+ 压缩引擎是 [`acp-kernel`](https://github.com/ranxianglei/acp-kernel)——平台无关的 MIT 库。它被内联打包进 `dist/index.js`,因此零运行时依赖。
58
196
 
59
- ## License
197
+ ## 许可
60
198
 
61
- MIT © [ranxianglei](https://github.com/ranxianglei)
199
+ MIT © ranxianglei
@@ -0,0 +1,65 @@
1
+ import { complete } from "@oh-my-pi/pi-ai";
2
+ import type { ExtensionContext } from "@oh-my-pi/pi-coding-agent";
3
+ import { type CoreMessage, type CompressionState, type CompressibleRange, type Prompts } from "acp-kernel";
4
+ import { type AgentMessage } from "./messages.js";
5
+ /** Reads `compressModel` from `~/.<CONFIG_DIR_NAME>/acp-omp.json as `provider:modelId`. */
6
+ export declare function readCompressModel(): string | null;
7
+ export declare function resolveCompressModel<T extends {
8
+ provider: string;
9
+ id: string;
10
+ }>(registry: {
11
+ find(provider: string, modelId: string): T | undefined;
12
+ }, currentModel: T | undefined, configured: string | null): {
13
+ model: T;
14
+ label: string;
15
+ } | null;
16
+ export declare function sliceRange(messages: CoreMessage[], state: CompressionState, startRef: string, endRef: string): CoreMessage[];
17
+ /** Pick the compressible span to compress. The kernel's recommended ranges are
18
+ * small groups (split at user boundaries and protected gaps) that routinely
19
+ * fall below `minCompressRange`, which the kernel would reject. Seed on the
20
+ * largest range and expand to adjacent ranges (smallest gap first) until the
21
+ * span covers enough text — counting every message in the span, matching the
22
+ * kernel's own validation. Returns null when even the whole compressible set
23
+ * is below the threshold. */
24
+ export declare function selectRangeSpan(ranges: CompressibleRange[], messages: CoreMessage[], state: CompressionState, minChars: number): {
25
+ startRef: string;
26
+ endRef: string;
27
+ tokens: number;
28
+ } | null;
29
+ export declare function formatSlice(slice: CoreMessage[], state: CompressionState): string;
30
+ export declare function parseSummary(text: string): string | null;
31
+ /** Build the summary-generation system prompt FROM the kernel's load-bearing
32
+ * compression rules (the same `Prompts` the compress tool and tier-1
33
+ * compression use), so /compact honors `acp-omp.json` prompt overrides and stays
34
+ * consistent with the rest of the ACP pipeline. `/compact` compresses an old
35
+ * contiguous range, so the tier-1 `howToCompressRules` are the right rule
36
+ * set (not tier-2/tier-3 distillation rules). */
37
+ export declare function buildSummaryPrompt(prompts: Prompts): string;
38
+ /** Generate ONE summary covering the FULL set of messages omp is about to
39
+ * discard on /compact (messagesToSummarize + turnPrefixMessages), matching
40
+ * native compaction semantics — the compaction entry omp stores afterwards
41
+ * is the durable record, and it truncates everything before firstKeptEntryId
42
+ * from the LLM view, so the summary must cover all of it. Kernel blocks are
43
+ * deliberately NOT used here: fold blocks only replay from in-stream
44
+ * compress tool calls, which the truncation removes. `previousSummary` (an
45
+ * earlier compaction's summary) is folded in so iterative compactions never
46
+ * drop it. Returns null on any failure so the caller falls back to Pi's
47
+ * native compaction. */
48
+ export declare function summarizeMessages(ctx: ExtensionContext, messages: AgentMessage[], prompts: Prompts, configuredModel?: string | null, opts?: {
49
+ previousSummary?: string;
50
+ customInstructions?: string;
51
+ signal?: AbortSignal;
52
+ completeFn?: typeof complete;
53
+ }): Promise<{
54
+ summary: string;
55
+ model: string;
56
+ } | null>;
57
+ /** Generate a summary for a message range using the compression model. Shared
58
+ * entry point for the `/compact` handler. Returns null when no model is
59
+ * usable, the slice is empty, the model is unauthenticated, or the response
60
+ * is unparseable — the caller then returns `undefined` so Pi falls back to
61
+ * its native compaction. */
62
+ export declare function summarizeRange(ctx: ExtensionContext, messages: CoreMessage[], state: CompressionState, startRef: string, endRef: string, prompts: Prompts, configuredModel?: string | null): Promise<{
63
+ summary: string;
64
+ model: string;
65
+ } | null>;
@@ -0,0 +1,8 @@
1
+ import type { RegisteredCommand } from "@oh-my-pi/pi-coding-agent";
2
+ import type { AcpRuntime } from "./runtime.js";
3
+ type CommandOptions = Omit<RegisteredCommand, "name" | "sourceInfo">;
4
+ export declare function makeCommands(runtime: AcpRuntime): Array<{
5
+ name: string;
6
+ options: CommandOptions;
7
+ }>;
8
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { ExtensionContext } from "@oh-my-pi/pi-coding-agent";
2
+ /**
3
+ * Host compatibility layer for pi vs omp (oh-my-pi) API differences.
4
+ *
5
+ * pi: systemPrompt is string, getSystemPrompt() returns string
6
+ * omp: systemPrompt is string[], getSystemPrompt() returns string[]
7
+ *
8
+ * These helpers normalize the differences so the rest of the codebase
9
+ * can work with a consistent string interface.
10
+ */
11
+ /** Normalize systemPrompt to a single string (join with newlines if array). */
12
+ export declare function normalizeSystemPrompt(input: string | string[] | undefined): string;
13
+ /**
14
+ * Format systemPrompt for the before_agent_start event handler.
15
+ * omp's BeforeAgentStartEventResult.systemPrompt is string[]: each entry is a
16
+ * prompt segment. We normalize the incoming base (string | string[]) to one
17
+ * string, append the ACP block, and return it as a single-element array.
18
+ */
19
+ export declare function formatSystemPromptForEvent(base: string | string[], append: string): string[];
20
+ /**
21
+ * Get the system prompt as a single string, regardless of host type.
22
+ * Handles both pi (string) and omp (string[]) return types.
23
+ */
24
+ export declare function getSystemPromptText(ctx: ExtensionContext): string;
@@ -0,0 +1,27 @@
1
+ import type { ToolDefinition } from "@oh-my-pi/pi-coding-agent";
2
+ import type { AcpRuntime } from "./runtime.js";
3
+ /** Label shown for a block when the model did not pass a topic: first
4
+ * sentence-ish slice of the summary (≤30 chars). Decorative only — never
5
+ * blocks compression. */
6
+ export declare function topicFallback(summary: string): string;
7
+ declare const CompressParams: import("@oh-my-pi/omptype").FluentType<{
8
+ content: {
9
+ endId: string;
10
+ startId: string;
11
+ summary: string;
12
+ topic?: string | undefined;
13
+ }[];
14
+ summaryMaxChars?: number | undefined;
15
+ topic?: string | undefined;
16
+ }, {
17
+ content: {
18
+ endId: string;
19
+ startId: string;
20
+ summary: string;
21
+ topic?: string | undefined;
22
+ }[];
23
+ summaryMaxChars?: number | undefined;
24
+ topic?: string | undefined;
25
+ }>;
26
+ export declare function makeCompressTool(runtime: AcpRuntime): ToolDefinition<typeof CompressParams>;
27
+ export {};