@lienat/pi-jev-compaction 0.1.0 → 0.1.1

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.en.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # pi-jev-compaction
2
2
 
3
3
  A [Pi](https://github.com/badlogic/pi-mono) extension for context compaction.
4
- The source code here is adapted from the native Claude Code plugin
5
- [`fast-jev-compaction`](https://github.com/tamaratran/fast-jev-compaction) by
6
- Tamara Tran, reworked to integrate with Pi. It filters out stale tool calls
7
- and their results before Pi runs its native compaction summary.
8
-
9
- This package is a **Pi adapter**, not a replacement for Pi's compaction system.
10
- Jev makes the keep/drop/truncate decisions; Pi still creates the summary,
11
- compaction entry, file-operation details, and retained recent context.
4
+ It uses `fast-jev-compaction@0.4.0` for Jev's tool-call decisions and adapts
5
+ those decisions to Pi's native message and compaction lifecycle.
6
+
7
+ The `0.4.0` dependency is the
8
+ [`aleksvega/fast-jev-compaction`](https://github.com/aleksvega/fast-jev-compaction)
9
+ fork based on
10
+ [`tamaratran/fast-jev-compaction`](https://github.com/tamaratran/fast-jev-compaction).
11
+ This project is a **Pi adapter**, not a replacement for Pi's compaction system:
12
+ Jev decides which eligible tool calls and results to keep, drop, or shorten;
13
+ Pi still creates the summary, compaction entry, file-operation details, and
14
+ retained recent context.
12
15
 
13
16
  ## Demo
14
17
 
@@ -43,17 +46,17 @@ Jev reviews eligible tool call/result pairs
43
46
  ```
44
47
 
45
48
  Rather than serializing Jev's simplified transcript back into Pi, the adapter
46
- maps Jev's decisions onto the original Pi messages. This means Pi's thinking
47
- blocks, images, tool-call metadata, and other native message data are never
48
- downgraded to plain text by the adapter.
49
+ maps Jev's decisions onto the original Pi messages. Pi's thinking blocks,
50
+ images, tool-call metadata, and other native message data therefore remain
51
+ native in the final preparation.
49
52
 
50
53
  The adapter only modifies:
51
54
 
52
55
  - `messagesToSummarize`
53
56
  - `turnPrefixMessages`
54
57
 
55
- All other compaction metadata remains fully owned by Pi and is never touched
56
- by this extension, including:
58
+ All other compaction metadata remains owned by Pi and is never touched by this
59
+ extension, including:
57
60
 
58
61
  - `firstKeptEntryId`
59
62
  - `tokensBefore`
@@ -62,23 +65,42 @@ by this extension, including:
62
65
  - `settings`
63
66
  - `isSplitTurn`
64
67
 
68
+ Pi computes some of this metadata before the hook runs. Consequently, if Jev
69
+ removes an old `write` or `edit` call, `fileOps` may still describe that call;
70
+ the extension does not recalculate Pi's native metadata.
71
+
65
72
  ### What can be filtered
66
73
 
67
74
  Only paired, text-only tool calls and tool results within the same native
68
- compaction input are eligible. The adapter leaves the following untouched:
75
+ compaction input are eligible. `messagesToSummarize` and
76
+ `turnPrefixMessages` are processed independently; they are never combined for
77
+ Jev decisions. Pi's separately retained recent region and messages outside
78
+ these two arrays are not sent to Jev.
79
+
80
+ The adapter leaves the following native messages or content untouched:
69
81
 
70
82
  - user and assistant prose;
71
- - thinking blocks and image content;
83
+ - thinking blocks and image content in the original Pi messages;
72
84
  - image-bearing tool results;
73
85
  - incomplete or duplicate call/result pairs;
74
86
  - pairs crossing the two native input boundaries;
75
87
  - Pi's separately retained recent region;
76
88
  - tool results without a valid matching call.
77
89
 
78
- A `drop_call` decision removes the call and its paired result. A
79
- `drop_result` decision keeps the call and truncates the result to the first
80
- 300 characters plus a note. Pi then summarizes the filtered native messages
81
- normally.
90
+ Ineligible tool results—unpaired results, duplicate IDs, image-bearing results,
91
+ or pairs crossing the two compaction inputs—are not converted into ordinary
92
+ user messages, and their text is excluded from Jev's temporary decision state.
93
+ The original Pi messages remain preserved.
94
+
95
+ A `drop_call` decision removes the call and its paired result. A `drop_result`
96
+ decision keeps the call. With the default `truncateHeadChars` of 300, a result
97
+ is shortened only when it is longer than 420 characters; shorter results stay
98
+ unchanged. Longer results keep their first 300 characters and receive a note.
99
+ Pi then summarizes the filtered native messages normally.
100
+
101
+ The `reviewed` count in notifications includes only tool calls actually queried
102
+ through Jev. It excludes calls kept by upstream protection rules (pinned) and
103
+ is not a count of questions or request batches.
82
104
 
83
105
  ## Compatibility
84
106
 
@@ -86,12 +108,11 @@ normally.
86
108
  - Node.js `>=24`
87
109
  - `fast-jev-compaction` `0.4.0`
88
110
 
89
- The extension has a strict Pi `0.85.1` version guard because the adapter
90
- relies on the mutable `session_before_compact` preparation object introduced
91
- in that release — this is not a formally documented extension API for
92
- replacing native preparation arrays. On any other Pi version the extension
93
- leaves the preparation untouched and Pi falls back to ordinary native
94
- compaction.
111
+ The extension has a strict Pi `0.85.1` version guard because the adapter relies
112
+ on the mutable `session_before_compact` preparation object used by that release.
113
+ This is not a formally documented extension API for replacing native
114
+ preparation arrays. On any other Pi version the extension leaves the
115
+ preparation untouched and Pi falls back to ordinary native compaction.
95
116
 
96
117
  This package does not modify Pi's source code or call Pi's exported
97
118
  `compact()` function itself.
@@ -147,8 +168,8 @@ use this extension if you are comfortable sending that information to TypeSafe.
147
168
 
148
169
  ### Timeout
149
170
 
150
- `PI_FAST_JEV_TIMEOUT_MS` controls the total time allowed for a single Jev
151
- attempt. It defaults to 15 seconds:
171
+ `PI_FAST_JEV_TIMEOUT_MS` controls the total time allowed for one Jev attempt,
172
+ including both native preparation arrays. It defaults to 15 seconds:
152
173
 
153
174
  ```sh
154
175
  export PI_FAST_JEV_TIMEOUT_MS=15000
@@ -177,11 +198,6 @@ npm run typecheck
177
198
  npm test
178
199
  ```
179
200
 
180
- Tests use injected Jev askers and timers and make no calls to TypeSafe or any
181
- main model. Coverage includes message mapping, tool-call/result pairing, image
182
- and thinking preservation, split compaction inputs, timeout and cancellation,
183
- native fallback, and Pi hook behavior.
184
-
185
201
  Build the package:
186
202
 
187
203
  ```sh
@@ -196,11 +212,12 @@ npm pack --ignore-scripts
196
212
 
197
213
  ## Acknowledgements and attribution
198
214
 
199
- This adapter is built on top of
200
- [`fast-jev-compaction`](https://github.com/tamaratran/fast-jev-compaction) by
201
- [Tamara Tran](https://github.com/tamaratran), and gratefully acknowledges her
202
- work. The upstream project provides the Jev decision logic that this Pi
203
- adapter integrates with native Pi compaction.
215
+ This adapter uses `fast-jev-compaction@0.4.0`, the
216
+ [`aleksvega/fast-jev-compaction`](https://github.com/aleksvega/fast-jev-compaction)
217
+ fork based on [`fast-jev-compaction`](https://github.com/tamaratran/fast-jev-compaction)
218
+ by [Tamara Tran](https://github.com/tamaratran). The upstream project provides
219
+ the Jev decision logic that this Pi adapter integrates with native Pi
220
+ compaction.
204
221
 
205
222
  If you believe this repository contains material that infringes your rights,
206
223
  or if the attribution needs to be corrected, please open an issue at
package/README.md CHANGED
@@ -1,11 +1,10 @@
1
1
  # pi-jev-compaction
2
2
 
3
-
4
3
  English version: [README.en.md](README.en.md)
5
4
 
6
- 一个用于上下文压缩之前 [Pi](https://github.com/badlogic/pi-mono) 的扩展,此处的源码来源于[`fast-jev-compaction`](https://github.com/tamaratran/fast-jev-compaction)的原生 claude code 的插件,针对 pi 的情况进行了适配。它会在 Pi 执行原生上下文压缩前,筛选不再需要的工具调用和工具结果。
5
+ 一个用于上下文压缩之前 [Pi](https://github.com/badlogic/pi-mono) 的扩展,核心判断逻辑复用了来源于[`fast-jev-compaction`](https://github.com/tamaratran/fast-jev-compaction)原作者的原生 claude code 的插件,针对 pi 的情况进行了适配。它会在 Pi 执行原生上下文压缩前,筛选不再需要的工具调用和工具结果,最终的压缩逻辑还是交给 Pi 原生的 `/compact()` 管理。
7
6
 
8
- 本项目是 **Pi 适配层**,不是对 Pi 原生压缩系统的替换。Jev 只负责判断哪些工具调用应保留、删除或截短;最终摘要、压缩节点、文件操作信息和近期上下文仍由 Pi 原生 `compact()` 生成和管理。
7
+ 里面的决策策略由 Jev 模型负责判断哪些工具调用应保留、删除或截短,本插件不修改 Pi 源码,也不会自行调用 Pi 导出的 `compact()` 函数。
9
8
 
10
9
  ## 视频演示
11
10
 
@@ -13,6 +12,7 @@ English version: [README.en.md](README.en.md)
13
12
 
14
13
  视频来源:[Tamara Tran 在 X 发布的视频](https://x.com/tamarajtran/status/2100694549362553153)。GIF 仅作为项目演示素材保留,版权归原作者所有。
15
14
 
15
+
16
16
  ## 工作方式
17
17
 
18
18
  手动执行 `/compact` 或 Pi 自动触发压缩时,流程如下:
@@ -23,14 +23,21 @@ Pi 准备压缩
23
23
  session_before_compact
24
24
 
25
25
  Jev 检查符合条件的工具调用/结果对
26
+
26
27
  ├─ 成功:只替换 Pi 原生的待摘要消息数组
27
28
  │ ↓
28
29
  │ Pi 原生 compact()
30
+
29
31
  └─ 超时、报错或响应无效:保持原始数组不变
30
32
 
31
33
  Pi 原生 compact()
32
34
  ```
33
35
 
36
+
37
+ ## 数据管理
38
+
39
+ Jev 并不会看到完整的 Pi 会话。它只处理 Pi 本次压缩提供的 `messagesToSummarize` 和 `turnPrefixMessages`,且这两组消息分别处理。
40
+
34
41
  适配层不会把 Jev 的简化 transcript 重新拼回 Pi,而是将 Jev 的决策映射回原始 Pi 消息。因此,Pi 的 thinking、图片、工具调用元数据和其他原生消息数据不会因为适配而被转换成普通文字。
35
42
 
36
43
  适配层只会修改:
@@ -60,17 +67,22 @@ Jev 检查符合条件的工具调用/结果对
60
67
  - Pi 单独保留的近期消息区域;
61
68
  - 没有有效调用对应的工具结果。
62
69
 
63
- 如果 Jev 返回 `drop_call`,扩展会删除调用及其对应结果。如果返回 `drop_result`,则保留调用,并将结果截短为前 300 个字符加上一条说明。处理完成后,Pi 仍会照常生成原生摘要。
70
+ 不符合筛选条件的工具结果(未配对、重复 ID、包含图片,或配对跨越两组压缩输入)不会被转换成普通用户消息,其文本也不会进入 Jev 的临时决策状态;原始 Pi 消息仍然保留。
71
+
72
+ 如果 Jev 返回 `drop_call`,扩展会删除调用及其对应结果;
73
+ 如果返回 `drop_result`,则保留调用。如果 Jev 判断只需保留工具调用而不需要完整结果,扩展会沿用上游规则:默认情况下,只有结果超过 420 个字符时才会截短为前 300 个字符并追加说明;较短结果会保持原文。
74
+
75
+ 处理完成后,Pi 仍会照常生成原生摘要。
76
+
77
+ 通知中的 `reviewed` 只统计实际向 Jev 询问的工具调用,不包含被上游保护规则固定保留的调用(pinned),也不是问题数或请求批次数。
64
78
 
65
79
  ## 兼容性
66
80
 
67
81
  - Pi `0.85.1`
68
82
  - Node.js `>=24`
69
- - `fast-jev-compaction` `0.4.0`
70
83
 
71
- 扩展对 Pi 版本进行严格检查,因为当前适配依赖 Pi `0.85.1` 的 `session_before_compact` preparation 对象可变行为;这不是 Pi 正式承诺的“替换原生压缩输入”扩展接口。使用其他 Pi 版本时,扩展不会修改压缩输入,Pi 会安全地回退到原生压缩。
84
+ 扩展对 Pi 版本进行严格检查,因为当前适配依赖 Pi `0.85.1` 的 `session_before_compact` preparation 对象可变行为;并没有替换原生压缩输入的扩展接口。所以使用其他 Pi 版本时,扩展不会修改压缩输入,Pi 会安全地回退到原生压缩。
72
85
 
73
- 本项目不修改 Pi 源码,也不会自行调用 Pi 导出的 `compact()` 函数。
74
86
 
75
87
  ## 安装
76
88
 
@@ -124,7 +136,7 @@ API Key 只在运行时读取,并只用于认证 Jev 请求;不会写入本
124
136
  export PI_FAST_JEV_TIMEOUT_MS=15000
125
137
  ```
126
138
 
127
- 如果 API Key 缺失、Pi 版本不支持、Jev 超时、请求失败或响应无效,扩展会显示简短提示并保持原始压缩输入不变,然后由 Pi 执行普通原生压缩。扩展不会递归调用 `/compact`。
139
+ 如果 API Key 缺失、Pi 版本不支持、Jev 超时、请求失败或响应无效,扩展会显示简短提示并保持原始压缩输入不变,然后由 Pi 执行普通原生压缩。
128
140
 
129
141
  用户主动取消压缩时,取消信号会传递给 Jev,扩展不会替换或修改原生压缩输入。
130
142
 
@@ -143,8 +155,6 @@ npm run typecheck
143
155
  npm test
144
156
  ```
145
157
 
146
- 测试使用注入的 Jev asker 和计时器,不会访问 TypeSafe,也不会调用主模型。测试覆盖消息映射、工具调用/结果配对、图片和 thinking 保留、两组压缩输入、超时、取消、原生回退和 Pi hook 行为。
147
-
148
158
  构建:
149
159
 
150
160
  ```sh
@@ -165,6 +175,8 @@ npm pack --ignore-scripts
165
175
  如果你认为本仓库包含侵犯你权利的内容,或署名信息需要修正,请在
166
176
  [GitHub Issues](https://github.com/Jul1en-Lin/pi-jev-compaction/issues) 联系维护者。我们会审核请求,并在适当情况下删除或修订相关内容。
167
177
 
178
+ 欢迎大家使用并指出指导性意见,感谢!
179
+
168
180
  ## 许可证
169
181
 
170
182
  本适配层使用 MIT License。使用时也请查看上游
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lienat/pi-jev-compaction",
3
- "version": "0.1.0",
4
- "description": "Apply fast-jev-compaction decisions before Pi's native compaction summary.",
3
+ "version": "0.1.1",
4
+ "description": "Pi extension that uses TypeSafe Jev to prune eligible historical tool calls before native compaction, preserving native messages and falling back safely on failure.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
package/src/adapter.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  compact,
3
+ resolveOptions,
3
4
  buildJevRequest,
4
5
  parseJevResponse,
5
6
  type CallDecision,
@@ -217,17 +218,23 @@ function toJevMessages(messages: readonly AgentMessage[], pairs: readonly Pair[]
217
218
  };
218
219
  }
219
220
 
220
- if (message.role === "toolResult" && eligible.has(message.toolCallId)) {
221
- return {
222
- role: "user",
223
- text: "",
224
- toolUses: [],
225
- toolResults: [{
226
- tool_use_id: message.toolCallId,
227
- text: contentText(message.content),
228
- isError: message.isError,
229
- }],
230
- };
221
+ if (message.role === "toolResult") {
222
+ if (eligible.has(message.toolCallId)) {
223
+ return {
224
+ role: "user",
225
+ text: "",
226
+ toolUses: [],
227
+ toolResults: [{
228
+ tool_use_id: message.toolCallId,
229
+ text: contentText(message.content),
230
+ isError: message.isError,
231
+ }],
232
+ };
233
+ }
234
+
235
+ // Unpaired or ineligible results must not become user text or goals in
236
+ // Jev's state; the original Pi message is preserved on output.
237
+ return { role: "user", text: "", toolUses: [] };
231
238
  }
232
239
 
233
240
  return { role: "user", text: messageText(message), toolUses: [] };
@@ -345,13 +352,18 @@ export async function filterMessages(
345
352
  return { messages: [...messages], changed: false, candidateCalls: 0, droppedCalls: 0, truncatedResults: 0 };
346
353
  }
347
354
 
348
- const compactOptions = options.compactOptions ?? {};
355
+ const compactOptions = resolveOptions(options.compactOptions ?? {});
349
356
  const result = await rejectOnAbort(
350
357
  compact(toJevMessages(messages, pairs), options.asker, compactOptions),
351
358
  options.signal,
352
359
  );
353
- const headChars = compactOptions.truncateHeadChars ?? 300;
354
- return applyActions(messages, decisionActions(result.decisions, pairs), headChars, pairs.length);
360
+ const reviewedCalls = result.decisions.filter((decision) => decision.reason !== "pinned").length;
361
+ return applyActions(
362
+ messages,
363
+ decisionActions(result.decisions, pairs),
364
+ compactOptions.truncateHeadChars,
365
+ reviewedCalls,
366
+ );
355
367
  }
356
368
 
357
369
  /** Filter Pi's two native summary inputs independently; their boundaries stay intact. */