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:
Greg Harvell
2026-04-14 14:04:24 -04:00
parent 7cc2b5bbbe
commit 9812beb817
4 changed files with 48 additions and 11 deletions
+17
View File
@@ -1,5 +1,22 @@
# 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
### Fixed
+7
View File
@@ -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.
## Contributors
[![complexthings](https://github.com/complexthings.png?size=50)](https://github.com/complexthings) [@complexthings](https://github.com/complexthings)
[![wassname](https://github.com/wassname.png?size=50)](https://github.com/wassname) [@wassname](https://github.com/wassname)
Full contributor list: https://github.com/complexthings/pi-dynamic-context-pruning/graphs/contributors
+23 -10
View File
@@ -81,17 +81,30 @@ export default function (pi: ExtensionAPI) {
const data = entry.data as any
if (data?.compressionBlocks) {
// Filter out blocks with corrupted (null/NaN/Infinity) timestamps —
// these were caused by Infinity anchorTimestamp values that became
// null after JSON round-trip.
const validBlocks = data.compressionBlocks.filter(
(b: any) =>
Number.isFinite(b.startTimestamp) &&
Number.isFinite(b.endTimestamp) &&
Number.isFinite(b.anchorTimestamp),
)
// Filter out blocks with corrupted timestamps, then repair
// anchorTimestamp which is legitimately Infinity for blocks that
// extend to end-of-conversation (JSON round-trips Infinity as null).
const validBlocks = data.compressionBlocks
.filter(
(b: any) =>
Number.isFinite(b.startTimestamp) &&
Number.isFinite(b.endTimestamp),
)
.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.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.totalPruneCount = data.totalPruneCount ?? 0
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@complexthings/pi-dynamic-context-pruning",
"version": "1.0.6",
"version": "1.0.7",
"description": "PI coding agent extension — Dynamic Context Pruning (DCP)",
"keywords": [
"pi-package",