mirror of
https://github.com/wassname/pi-dynamic-context-pruning.git
synced 2026-06-27 16:46:12 +08:00
chore(release): 1.0.7
- Fix Infinity anchorTimestamp repair on JSON round-trip (blocks extending to end-of-conversation now restored correctly instead of discarded) - Fix nextBlockId calculation to use max(id)+1 instead of array length - Rename internal prompt tag <dcp-message-id> to <dcp-id> - Add regression tests for corrupted-block resilience (PR #3, @wassname) - Update README with Contributors section - Update CHANGELOG for 1.0.7 - Bump package.json to 1.0.7 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.0.7] - 2026-04-14
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Infinity anchorTimestamp ghost block spiral** — When a `compress` range extended to the end of the conversation, `resolveAnchorTimestamp` returned `Infinity`. `JSON.stringify(Infinity)` serialises to `null`, so on session restore the corrupted block's timestamps coerced to `0` in JS overlap checks, making every new range appear to overlap the ghost block and trapping the model in a compression spiral (101 failures over 2 hours). `resolveAnchorTimestamp` now returns `endTimestamp + 1` instead of `Infinity`.
|
||||||
|
- **Corrupted block propagation on session restore** — `index.ts` now filters out any persisted compression block whose `startTimestamp`, `endTimestamp`, or `anchorTimestamp` is non-finite before restoring state, preventing ghost blocks from surviving across sessions.
|
||||||
|
- **Non-finite timestamp guard** — All code paths that create or apply compression blocks now validate timestamps are finite before proceeding, failing fast rather than silently corrupting state.
|
||||||
|
- **Overlap error diagnostics** — Overlap error messages now include the existing block's timestamp range to aid debugging.
|
||||||
|
- **Prompt tag name mismatch** — The prompt tag was named `<dcp-message-id>` but the code injected `<dcp-id>`; tag name corrected to `<dcp-id>` throughout `prompts.ts`.
|
||||||
|
- **Duplicate test** — Removed a duplicate test case from `pruner.test.ts`.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Regression tests** — New test cases for the `Infinity` anchor scenario, `null`-timestamp corrupted blocks, and corrupted-block resilience on session restore.
|
||||||
|
|
||||||
|
Thanks to [@wassname](https://github.com/wassname) for diagnosing and fixing the compression spiral root cause in [#3](https://github.com/complexthings/pi-dynamic-context-pruning/pull/3).
|
||||||
|
|
||||||
## [1.0.6] - 2026-04-09
|
## [1.0.6] - 2026-04-09
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -148,3 +148,10 @@ npx tsc --noEmit # type-check without emitting
|
|||||||
```
|
```
|
||||||
|
|
||||||
The extension is loaded by pi via [jiti](https://github.com/unjs/jiti) so TypeScript is executed directly — no build step required for normal use.
|
The extension is loaded by pi via [jiti](https://github.com/unjs/jiti) so TypeScript is executed directly — no build step required for normal use.
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
[](https://github.com/complexthings) [@complexthings](https://github.com/complexthings)
|
||||||
|
[](https://github.com/wassname) [@wassname](https://github.com/wassname)
|
||||||
|
|
||||||
|
Full contributor list: https://github.com/complexthings/pi-dynamic-context-pruning/graphs/contributors
|
||||||
|
|||||||
@@ -81,17 +81,30 @@ export default function (pi: ExtensionAPI) {
|
|||||||
const data = entry.data as any
|
const data = entry.data as any
|
||||||
|
|
||||||
if (data?.compressionBlocks) {
|
if (data?.compressionBlocks) {
|
||||||
// Filter out blocks with corrupted (null/NaN/Infinity) timestamps —
|
// Filter out blocks with corrupted timestamps, then repair
|
||||||
// these were caused by Infinity anchorTimestamp values that became
|
// anchorTimestamp which is legitimately Infinity for blocks that
|
||||||
// null after JSON round-trip.
|
// extend to end-of-conversation (JSON round-trips Infinity as null).
|
||||||
const validBlocks = data.compressionBlocks.filter(
|
const validBlocks = data.compressionBlocks
|
||||||
|
.filter(
|
||||||
(b: any) =>
|
(b: any) =>
|
||||||
Number.isFinite(b.startTimestamp) &&
|
Number.isFinite(b.startTimestamp) &&
|
||||||
Number.isFinite(b.endTimestamp) &&
|
Number.isFinite(b.endTimestamp),
|
||||||
Number.isFinite(b.anchorTimestamp),
|
|
||||||
)
|
)
|
||||||
|
.map((b: any) => ({
|
||||||
|
...b,
|
||||||
|
// anchorTimestamp is Infinity when the block extends to the end
|
||||||
|
// of the conversation; JSON round-trips Infinity as null, so
|
||||||
|
// repair it here rather than discarding the block.
|
||||||
|
anchorTimestamp: Number.isFinite(b.anchorTimestamp)
|
||||||
|
? b.anchorTimestamp
|
||||||
|
: Infinity,
|
||||||
|
}))
|
||||||
state.compressionBlocks = validBlocks
|
state.compressionBlocks = validBlocks
|
||||||
state.nextBlockId = data.nextBlockId ?? state.compressionBlocks.length
|
state.nextBlockId =
|
||||||
|
data.nextBlockId ??
|
||||||
|
(state.compressionBlocks.length > 0
|
||||||
|
? Math.max(0, ...state.compressionBlocks.map((b: any) => b.id)) + 1
|
||||||
|
: 1)
|
||||||
state.tokensSaved = data.tokensSaved ?? 0
|
state.tokensSaved = data.tokensSaved ?? 0
|
||||||
state.totalPruneCount = data.totalPruneCount ?? 0
|
state.totalPruneCount = data.totalPruneCount ?? 0
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@complexthings/pi-dynamic-context-pruning",
|
"name": "@complexthings/pi-dynamic-context-pruning",
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"description": "PI coding agent extension — Dynamic Context Pruning (DCP)",
|
"description": "PI coding agent extension — Dynamic Context Pruning (DCP)",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"pi-package",
|
"pi-package",
|
||||||
|
|||||||
Reference in New Issue
Block a user