@lll9p/pi-better-compaction 0.2.0 → 0.4.0

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,109 +1,56 @@
1
1
  # pi-better-compaction
2
2
 
3
- A Pi extension that upgrades compaction with two coordinated strategies:
3
+ English | [中文](README.zh-CN.md)
4
4
 
5
- 1. **OpenAI Responses APIs** (`openai-responses`, `openai-codex-responses`) use the provider's
6
- native `/responses/compact` endpoint, then replay the opaque compacted window on later
7
- requests without patching Pi core.
8
- 2. **Every other API** (Anthropic, Gemini, etc.) runs Pi's own native compaction method,
9
- optionally driven by a **dedicated compaction model** so you can summarize with a cheaper/faster
10
- model than the one you are chatting with.
5
+ A [pi](https://github.com/nicepkg/pi) extension that upgrades context compaction with two coordinated strategies:
11
6
 
12
- Everything fails open: if any step cannot proceed, the extension returns control to Pi's default
13
- compaction so a compaction never breaks because of this extension.
7
+ 1. **OpenAI Responses APIs** use the provider's native compaction endpoint, preserving opaque context that plain text summaries lose.
8
+ 2. **All other APIs** (Anthropic, Gemini, etc.) can run pi's built-in compaction with a **dedicated cheaper/faster model**, so summarization doesn't consume quota on your primary model.
14
9
 
15
- ## Requirements
16
-
17
- - **Minimum Pi version:** `@earendil-works/pi-coding-agent >= 0.80.0`
18
-
19
- This extension relies on `modelRegistry.getApiKeyAndHeaders(model)` and the exported native
20
- `compact()` function.
21
-
22
- ## Behavior
23
-
24
- The `session_before_compact` decision tree:
25
-
26
- ```
27
- session_before_compact
28
-
29
- ├─ config.enabled == false ───────────────────────► Pi default compaction
30
-
31
- ├─ current model API is a Responses API
32
- │ │ (openai-responses / openai-codex-responses, narrowable via config)
33
- │ ├─ POST /responses/compact
34
- │ │ ├─ success ─────────────────────────────────► store opaque window + real summary
35
- │ │ ├─ user aborted ─────────────────────────────► cancel
36
- │ │ └─ failure (404 / network / malformed) ──────► fall through ▼
37
- │ └─ (missing base URL / API key) ─────────────────► fall through ▼
38
-
39
- ├─ config.compactionModel is set and resolvable and ≠ current model
40
- │ └─ run Pi's native compact() with that model ────► use its result
41
-
42
- └─ otherwise ─────────────────────────────────────► Pi default compaction
43
- (no model configured, or it equals the current model — Pi runs the
44
- same native method itself, keeping its streaming progress UI)
45
- ```
46
-
47
- On the next supported Responses request after a native `/responses/compact`, the
48
- `before_provider_request` hook rewrites Pi's summary-oriented replay into:
49
-
50
- - fresh current prompt envelope
51
- - stored opaque compacted window
52
- - live post-compaction tail
53
-
54
- Requests produced by the native-method fallback carry Pi's standard `{readFiles, modifiedFiles}`
55
- details, so they replay through Pi's default path — no rewrite, no special handling.
56
-
57
- ### Selection is by API, not provider
58
-
59
- Any provider speaking a Responses API gets a native compact attempt, including OpenAI-compatible
60
- proxies with a custom `baseUrl`. If such an endpoint does not implement `/responses/compact`, the
61
- request 404s and the extension fails through to the configured fallback model (or Pi default). To
62
- avoid the probe entirely for one API, narrow `responsesCompactApis`.
10
+ Everything fails open — if any step cannot proceed, pi's default compaction takes over.
63
11
 
64
12
  ## Install
65
13
 
66
- From npm (recommended):
67
-
68
14
  ```bash
15
+ # From npm (recommended)
69
16
  pi install npm:@lll9p/pi-better-compaction
70
- ```
71
17
 
72
- Try it for a single run without installing:
73
-
74
- ```bash
18
+ # Try without installing
75
19
  pi -e npm:@lll9p/pi-better-compaction
76
- ```
77
-
78
- From a checkout (development):
79
20
 
80
- ```bash
21
+ # From source
81
22
  git clone https://github.com/lll9p/pi-better-compaction.git
82
- cd pi-better-compaction
83
- pi install .
23
+ cd pi-better-compaction && pi install .
84
24
  ```
85
25
 
86
26
  After installation, run `/reload`.
87
27
 
28
+ ## Requirements
29
+
30
+ - **pi** ≥ 0.84.3 (`@earendil-works/pi-coding-agent >= 0.84.3`)
31
+
88
32
  ## Configuration
89
33
 
90
- Single source, merged over built-in defaults:
34
+ Config file location:
91
35
 
92
36
  ```
93
37
  ~/.pi/agent/extensions/pi-better-compaction/config.json
94
38
  ```
95
39
 
96
- A missing file silently uses the defaults below. The extension never writes this file for you.
40
+ If the file doesn't exist, all defaults apply. The extension never creates this file.
97
41
 
98
- ```json
42
+ ### Defaults
43
+
44
+ ```jsonc
99
45
  {
100
46
  "enabled": true,
101
-
102
- "compactionModel": "openai/gpt-5.1-mini",
47
+ "compactionVersion": "v2",
48
+ "compactionModel": null,
103
49
  "compactionThinkingLevel": "off",
104
-
105
50
  "responsesCompactApis": ["openai-responses", "openai-codex-responses"],
51
+ "allowCompactionContinuityBreak": false,
106
52
 
53
+ // Debug & logging
107
54
  "notifyOnLoad": false,
108
55
  "debug": false,
109
56
  "logProviderPayloads": false,
@@ -113,30 +60,67 @@ A missing file silently uses the defaults below. The extension never writes this
113
60
  }
114
61
  ```
115
62
 
116
- | Key | Default | Description |
117
- |-----|---------|-------------|
118
- | `enabled` | `true` | Master switch. `false` → Pi default compaction everywhere. |
119
- | `compactionModel` | *(unset)* | `"provider/model-id"` used for native-method fallback (non-Responses APIs, or when the compact endpoint fails). `null`/unset → use the current model via Pi's default path. The provider is split on the first `/`, so model ids may contain slashes (e.g. `"openrouter/deepseek/deepseek-chat"`). |
120
- | `compactionThinkingLevel` | `"off"` | Thinking level passed to the native `compact()` fallback. One of `off, minimal, low, medium, high, xhigh, max`. |
121
- | `responsesCompactApis` | both Responses APIs | Which Responses APIs use the compact endpoint. May only narrow the built-in set; unknown entries are ignored with a warning. |
122
- | `notifyOnLoad` | `false` | Show a load notification in the TUI. |
123
- | `debug` | `false` | Write lifecycle + compaction-event artifacts. |
124
- | `logProviderPayloads` | `false` | Write `before_provider_request` payload artifacts. |
125
- | `logCompactResponses` | `false` | Write compact endpoint request/response artifacts. |
126
- | `redactSensitiveData` | `true` | Redact secrets in artifacts. Keep on. |
127
- | `artifactRoot` | `~/.pi/agent/artifacts/pi-better-compaction` | Debug artifact root. `~/` and relative paths (resolved against the config dir) are supported. |
63
+ ### Options reference
64
+
65
+ | Option | Type | Default | Description |
66
+ |--------|------|---------|-------------|
67
+ | `enabled` | `boolean` | `true` | Master switch. Set `false` to disable the extension entirely. |
68
+ | `compactionVersion` | `"v1" \| "v2"` | `"v2"` | Protocol for Responses-family APIs. **V2** (streaming, encrypted blob) is the current OpenAI default. **V1** uses the legacy `/responses/compact` endpoint. |
69
+ | `compactionModel` | `string \| null` | `null` | Model for fallback compaction (non-Responses APIs, or when native compact fails). Format: `"provider/model-id"`, e.g. `"openai/gpt-5.1-mini"`. `null` = let pi use the current chat model. |
70
+ | `compactionThinkingLevel` | `string` | `"off"` | Thinking level for the fallback compaction model. One of: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. |
71
+ | `responsesCompactApis` | `string[]` | `["openai-responses", "openai-codex-responses"]` | Which Responses APIs use native compaction. Can only narrow the built-in set; unknown entries are ignored with a warning. |
72
+ | `allowCompactionContinuityBreak` | `boolean` | `false` | Allow restarting native compaction when the latest session compaction was created by pi's default path (not this extension). Sacrifices opaque-window continuity at that boundary. |
73
+ | `notifyOnLoad` | `boolean` | `false` | Show a notification in the TUI when the extension loads. |
74
+ | `debug` | `boolean` | `false` | Write lifecycle and compaction-event debug artifacts. |
75
+ | `logProviderPayloads` | `boolean` | `false` | Write `before_provider_request` payload artifacts. |
76
+ | `logCompactResponses` | `boolean` | `false` | Write compact endpoint request/response artifacts. |
77
+ | `redactSensitiveData` | `boolean` | `true` | Redact secrets in debug artifacts. |
78
+ | `artifactRoot` | `string` | `"~/.pi/agent/artifacts/pi-better-compaction"` | Root directory for debug artifacts. Supports `~/` and relative paths (resolved against config dir). |
79
+
80
+ ### Example: use a cheap model for fallback compaction
81
+
82
+ ```json
83
+ {
84
+ "compactionModel": "openai/gpt-5.1-mini",
85
+ "compactionThinkingLevel": "off"
86
+ }
87
+ ```
88
+
89
+ ### Example: force V1 compaction protocol
90
+
91
+ ```json
92
+ {
93
+ "compactionVersion": "v1"
94
+ }
95
+ ```
96
+
97
+ ## How it works
98
+
99
+ When pi triggers compaction (`session_before_compact`):
100
+
101
+ 1. **Responses API detected** → run native compaction (V2 or V1 per config):
102
+ - **V2**: streams a request with `compaction_trigger` to `/responses`; the API returns an encrypted compaction blob. Retained user/developer messages + blob form the compacted context.
103
+ - **V1**: POSTs to `/responses/compact`; receives an opaque compacted window.
104
+ - On success, the compacted window is stored and replayed on subsequent requests via `before_provider_request`.
105
+
106
+ 2. **Not a Responses API, or native compact failed** → if `compactionModel` is configured and differs from the current model, run pi's built-in `compact()` with that model.
128
107
 
129
- ### Codex-aligned compact request
108
+ 3. **No fallback configured** → pi's default compaction runs as if the extension weren't installed.
130
109
 
131
- For Responses compaction, the extension mirrors the latest codex_rs `CompactionInput` fields
132
- (`tools`, `parallel_tool_calls`, `reasoning`, `service_tier`, `prompt_cache_key`, `text`) by
133
- capturing them from the most recent live provider request for the same model/session and attaching
134
- them to the compact request body. When no such request has been seen yet, the compact request falls
135
- back to the minimal `model` / `input` / `instructions` body.
110
+ Selection is by API type, not provider — any OpenAI-compatible proxy speaking a Responses API gets a native compact attempt. If the endpoint doesn't support it, the request fails and falls through to the configured fallback.
136
111
 
137
- ## Debug artifacts
112
+ ## Debugging
138
113
 
139
- Written per session under:
114
+ Enable debug artifacts:
115
+
116
+ ```json
117
+ {
118
+ "debug": true,
119
+ "logCompactResponses": true
120
+ }
121
+ ```
122
+
123
+ Then `/reload`, run `/compact`, send a follow-up message, and inspect:
140
124
 
141
125
  ```
142
126
  <artifactRoot>/sessions/<session-id>/
@@ -146,38 +130,13 @@ Written per session under:
146
130
  └── lifecycle/
147
131
  ```
148
132
 
149
- Troubleshooting flow:
150
-
151
- 1. set `debug: true` and `logCompactResponses: true` (keep `redactSensitiveData: true`)
152
- 2. `/reload`
153
- 3. run `/compact`, then send a follow-up message
154
- 4. inspect the newest artifact in the session directory
155
-
156
- ## Package structure
157
-
158
- ```
159
- package-root/
160
- ├── index.ts # entrypoint declared in package.json
161
- ├── src/
162
- │ ├── extension-runtime.ts # hook registration + compaction decision tree
163
- │ ├── config.ts # config.json loader (single source + defaults)
164
- │ ├── runtime.ts # API-based environment resolution + auth
165
- │ ├── compact-client.ts # /responses/compact client + summary extraction
166
- │ ├── native-fallback.ts # configured-model native compact() driver
167
- │ ├── request-context-cache.ts # captures codex-aligned fields from live requests
168
- │ ├── serializer.ts # Responses input serialization
169
- │ ├── payload-rewrite.ts # native opaque-window replay rewrite
170
- │ ├── details-store.ts # latest-valid native compaction lookup
171
- │ ├── debug.ts # artifact writing + redaction
172
- │ ├── supported-environment.ts # re-exports
173
- │ └── types.ts # config + persisted native-compaction types
174
- └── test/
175
- ```
176
-
177
133
  ## Tests
178
134
 
179
135
  ```bash
180
136
  bun test
181
137
  bun test --coverage --coverage-reporter=text --coverage-reporter=lcov
182
- bun test ./test/pi-smoke.test.ts
183
138
  ```
139
+
140
+ ## License
141
+
142
+ MIT
@@ -0,0 +1,142 @@
1
+ # pi-better-compaction
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 一个 [pi](https://github.com/nicepkg/pi) 扩展,通过两条策略提升上下文压缩效果:
6
+
7
+ 1. **OpenAI Responses 系列 API** 使用提供商原生压缩端点,保留纯文本摘要无法留存的不透明上下文。
8
+ 2. **其他所有 API**(Anthropic、Gemini 等)可用一个**独立的低成本模型**执行 pi 内置压缩,避免在主模型上消耗额度。
9
+
10
+ 所有环节都安全降级——任何步骤无法执行时,pi 的默认压缩自动接管。
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ # 从 npm 安装(推荐)
16
+ pi install npm:@lll9p/pi-better-compaction
17
+
18
+ # 临时试用,不安装
19
+ pi -e npm:@lll9p/pi-better-compaction
20
+
21
+ # 从源码安装
22
+ git clone https://github.com/lll9p/pi-better-compaction.git
23
+ cd pi-better-compaction && pi install .
24
+ ```
25
+
26
+ 安装后执行 `/reload` 生效。
27
+
28
+ ## 要求
29
+
30
+ - **pi** ≥ 0.84.3(`@earendil-works/pi-coding-agent >= 0.84.3`)
31
+
32
+ ## 配置
33
+
34
+ 配置文件路径:
35
+
36
+ ```
37
+ ~/.pi/agent/extensions/pi-better-compaction/config.json
38
+ ```
39
+
40
+ 文件不存在时使用默认值。扩展不会自动创建此文件。
41
+
42
+ ### 默认配置
43
+
44
+ ```jsonc
45
+ {
46
+ "enabled": true,
47
+ "compactionVersion": "v2",
48
+ "compactionModel": null,
49
+ "compactionThinkingLevel": "off",
50
+ "responsesCompactApis": ["openai-responses", "openai-codex-responses"],
51
+ "allowCompactionContinuityBreak": false,
52
+
53
+ // 调试与日志
54
+ "notifyOnLoad": false,
55
+ "debug": false,
56
+ "logProviderPayloads": false,
57
+ "logCompactResponses": false,
58
+ "redactSensitiveData": true,
59
+ "artifactRoot": "~/.pi/agent/artifacts/pi-better-compaction"
60
+ }
61
+ ```
62
+
63
+ ### 配置项说明
64
+
65
+ | 选项 | 类型 | 默认值 | 说明 |
66
+ |------|------|--------|------|
67
+ | `enabled` | `boolean` | `true` | 总开关。设为 `false` 完全禁用扩展。 |
68
+ | `compactionVersion` | `"v1" \| "v2"` | `"v2"` | Responses 系列 API 的压缩协议。**V2**(流式,加密 blob)是 OpenAI 当前默认协议;**V1** 使用旧版 `/responses/compact` 端点。 |
69
+ | `compactionModel` | `string \| null` | `null` | 回退压缩使用的模型(用于非 Responses API,或原生压缩失败时)。格式:`"provider/model-id"`,如 `"openai/gpt-5.1-mini"`。`null` = 由 pi 使用当前对话模型。 |
70
+ | `compactionThinkingLevel` | `string` | `"off"` | 回退压缩模型的思考级别。可选:`off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`。 |
71
+ | `responsesCompactApis` | `string[]` | `["openai-responses", "openai-codex-responses"]` | 启用原生压缩的 Responses API 列表。只能缩小内置集合,不能添加新值。 |
72
+ | `allowCompactionContinuityBreak` | `boolean` | `false` | 当会话最近一次压缩不是本扩展创建的时,是否允许重新开始原生压缩。会在该边界处牺牲不透明窗口的连续性。 |
73
+ | `notifyOnLoad` | `boolean` | `false` | 扩展加载时在 TUI 中显示通知。 |
74
+ | `debug` | `boolean` | `false` | 写入生命周期和压缩事件的调试文件。 |
75
+ | `logProviderPayloads` | `boolean` | `false` | 写入 `before_provider_request` 请求体调试文件。 |
76
+ | `logCompactResponses` | `boolean` | `false` | 写入压缩端点的请求/响应调试文件。 |
77
+ | `redactSensitiveData` | `boolean` | `true` | 在调试文件中脱敏。 |
78
+ | `artifactRoot` | `string` | `"~/.pi/agent/artifacts/pi-better-compaction"` | 调试文件根目录。支持 `~/` 和相对路径(相对于配置文件目录解析)。 |
79
+
80
+ ### 示例:使用低成本模型做回退压缩
81
+
82
+ ```json
83
+ {
84
+ "compactionModel": "openai/gpt-5.1-mini",
85
+ "compactionThinkingLevel": "off"
86
+ }
87
+ ```
88
+
89
+ ### 示例:强制使用 V1 压缩协议
90
+
91
+ ```json
92
+ {
93
+ "compactionVersion": "v1"
94
+ }
95
+ ```
96
+
97
+ ## 工作原理
98
+
99
+ pi 触发压缩时(`session_before_compact`):
100
+
101
+ 1. **检测到 Responses API** → 执行原生压缩(根据配置选择 V2 或 V1):
102
+ - **V2**:向 `/responses` 端点发送携带 `compaction_trigger` 的流式请求,API 返回加密压缩 blob。保留的用户/开发者消息 + blob 组成压缩后的上下文。
103
+ - **V1**:POST 到 `/responses/compact`,接收不透明的压缩窗口。
104
+ - 成功后,压缩窗口被存储,后续请求通过 `before_provider_request` 钩子回放。
105
+
106
+ 2. **非 Responses API,或原生压缩失败** → 若配置了 `compactionModel` 且与当前模型不同,使用该模型执行 pi 内置的 `compact()` 方法。
107
+
108
+ 3. **未配置回退模型** → pi 的默认压缩照常执行,如同扩展未安装。
109
+
110
+ 判断依据是 API 类型而非提供商——任何使用 Responses API 协议的 OpenAI 兼容代理都会触发原生压缩尝试。如果端点不支持,请求失败后自动回退。
111
+
112
+ ## 调试
113
+
114
+ 启用调试文件输出:
115
+
116
+ ```json
117
+ {
118
+ "debug": true,
119
+ "logCompactResponses": true
120
+ }
121
+ ```
122
+
123
+ 然后 `/reload`,执行 `/compact`,发送一条后续消息,检查:
124
+
125
+ ```
126
+ <artifactRoot>/sessions/<session-id>/
127
+ ├── provider-requests/
128
+ ├── compact-responses/
129
+ ├── compaction-events/
130
+ └── lifecycle/
131
+ ```
132
+
133
+ ## 测试
134
+
135
+ ```bash
136
+ bun test
137
+ bun test --coverage --coverage-reporter=text --coverage-reporter=lcov
138
+ ```
139
+
140
+ ## 许可证
141
+
142
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lll9p/pi-better-compaction",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Better compaction for pi: native /responses/compact replay for OpenAI Responses APIs, plus a configurable compaction model driving pi's native summarization everywhere else.",
6
6
  "author": "Lilin Lao",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "homepage": "https://github.com/lll9p/pi-better-compaction#readme",
16
16
  "publishConfig": {
17
- "access": "public"
17
+ "access": "public",
18
+ "registry": "https://registry.npmjs.org/"
18
19
  },
19
20
  "keywords": [
20
21
  "pi-package",
@@ -32,6 +33,7 @@
32
33
  "files": [
33
34
  "index.ts",
34
35
  "src/compact-client.ts",
36
+ "src/compact-client-v2.ts",
35
37
  "src/config.ts",
36
38
  "src/debug.ts",
37
39
  "src/details-store.ts",
@@ -39,11 +41,14 @@
39
41
  "src/native-fallback.ts",
40
42
  "src/payload-rewrite.ts",
41
43
  "src/request-context-cache.ts",
44
+ "src/retained-messages.ts",
42
45
  "src/runtime.ts",
43
46
  "src/serializer.ts",
47
+ "src/shared-headers.ts",
44
48
  "src/supported-environment.ts",
45
49
  "src/types.ts",
46
50
  "README.md",
51
+ "README.zh-CN.md",
47
52
  "LICENSE"
48
53
  ],
49
54
  "scripts": {
@@ -54,6 +59,6 @@
54
59
  "peerDependencies": {
55
60
  "@earendil-works/pi-agent-core": "*",
56
61
  "@earendil-works/pi-ai": "*",
57
- "@earendil-works/pi-coding-agent": ">=0.80.0"
62
+ "@earendil-works/pi-coding-agent": ">=0.84.3"
58
63
  }
59
64
  }