pi-condense 2.10.1 → 2.10.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/CHANGELOG.md +4 -0
- package/PRUNING.md +1 -1
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/commands.ts +1 -1
- package/src/config.test.ts +16 -0
- package/src/protected.test.ts +22 -0
- package/src/types.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,10 @@ Published to npm as [`pi-condense`](https://www.npmjs.com/package/pi-condense) (
|
|
|
7
7
|
Pushing a `vX.Y.Z` tag triggers `.github/workflows/release.yml`, which runs the tests and
|
|
8
8
|
publishes via OIDC trusted publishing. See `.agents/skills/release/SKILL.md`.
|
|
9
9
|
|
|
10
|
+
## [2.10.2] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
- Protect `gauntlet-overrides.md` reads by default alongside skill files, including `.pi/` and `doc/` paths, so per-repo harness contracts survive context pruning. User-supplied `protectedPaths` still replaces the defaults; path matching remains limited to `args.path`.
|
|
13
|
+
|
|
10
14
|
## [2.10.1] - 2026-09-02
|
|
11
15
|
|
|
12
16
|
- **Spill sidecar basenames capped at 255 bytes ([#14](https://github.com/jjuraszek/pi-condense/issues/14)).** Providers emitting 300+ char tool-call ids drove `blobPathFor` past the filesystem basename limit: eager spill failed silently (`ENAMETOOLONG` caught, oversized result stayed inline and bloated context) and the deterministic backfill aborted fail-closed. Fitting names stay byte-identical; over-limit names become `<234-byte sanitized prefix>.<16-hex sha1 of the unsanitized occurrence key>.txt` (exactly 255 bytes). The `.` separator is unreachable by `sanitizeId`, so capped names are namespace-disjoint from short-key names by construction - no probe, no migration, persisted `spillPath` read-back unchanged. Spec: `doc/specs/2026-09-02-gh-14-spill-filename-cap.md` (partially supersedes the 2026-06-02 spill spec's filename derivation).
|
package/PRUNING.md
CHANGED
|
@@ -618,7 +618,7 @@ A tool call is protected if **either** its `toolName` is in `protectedTools` **o
|
|
|
618
618
|
|
|
619
619
|
**`protectedTools: string[]`** (default `[]`) - allowlist of tool names. Covers tools whose output is a small handle that must be reused byte-for-byte (e.g. a session-id) or planning tools like `todowrite` / `todoread`.
|
|
620
620
|
|
|
621
|
-
**`protectedPaths: string[]`** (default `["**/skills/**/*.md"]`) - glob list matched against `args.path`. Designed for skill files that carry multi-step workflow gates; summarizing them is categorically lossy. Non-string or missing `path` arguments never match. Set `[]` to disable. Edit with `/pruner protected-paths`.
|
|
621
|
+
**`protectedPaths: string[]`** (default `["**/skills/**/*.md", "**/gauntlet-overrides.md"]`) - glob list matched against `args.path`. Designed for skill files that carry multi-step workflow gates; summarizing them is categorically lossy. Non-string or missing `path` arguments never match. Set `[]` to disable. Edit with `/pruner protected-paths`.
|
|
622
622
|
|
|
623
623
|
Glob contract: full-path match against the raw `args.path` string with `\` normalized to `/`. `*` and `?` match within a segment (no `/`); `**` crosses segments; `**/` also matches zero directories (so `**/SKILL.md` matches a bare relative `SKILL.md`). Case-sensitive. All other characters are regex-escaped literals.
|
|
624
624
|
|
package/README.md
CHANGED
|
@@ -169,9 +169,11 @@ Settings live under `contextPrune` in `<agent-dir>/settings.json` (`$PI_CODING_A
|
|
|
169
169
|
| `pruneOn` | `agent-message` | Trigger mode - see Architecture above |
|
|
170
170
|
| `autoBudgetThreshold` | `null` | Fraction (e.g. `0.8`) of the context window that force-flushes everything regardless of `pruneOn`; the trigger point is capped at 300k tokens |
|
|
171
171
|
| `frontierGapThresholdTokens` | `null` | Opt-in absolute-token flush trigger: fires at `turn_end` once the un-pruned tail past the prune frontier reaches N tokens, regardless of window size; recommended starting value `80000` |
|
|
172
|
-
| `protectedTools` / `protectedPaths` | `[]` / `["**/skills/**/*.md"]` | Tool names / path globs that are never pruned |
|
|
172
|
+
| `protectedTools` / `protectedPaths` | `[]` / `["**/skills/**/*.md", "**/gauntlet-overrides.md"]` | Tool names / path globs that are never pruned |
|
|
173
173
|
| `spillThreshold` | `65536` | Chars above which a single oversized result spills straight to a sidecar file |
|
|
174
174
|
|
|
175
|
+
The default also protects reads of [pi-gauntlet](https://github.com/jjuraszek/pi-gauntlet)'s per-repo `gauntlet-overrides.md` so the repo's harness contract stays available for gate decisions after pruning.
|
|
176
|
+
|
|
175
177
|
The full settings JSON, every key, the commands table, footer widget states, spilled-output details, and the summarizer-model-by-plan table live in **[doc/configuration.md](doc/configuration.md)**.
|
|
176
178
|
|
|
177
179
|
## Relationship to the rest of the platform
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-condense",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.2",
|
|
4
4
|
"description": "Pi coding-agent extension that summarizes completed tool-call batches, replaces raw outputs with short stubs, compresses closed tool-call chains, and recovers any original on demand via context_tree_query.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
package/src/commands.ts
CHANGED
|
@@ -256,7 +256,7 @@ function protectedToolsDescription(config: ContextPruneConfig): string {
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
function protectedPathsDescription(config: ContextPruneConfig): string {
|
|
259
|
-
return `Glob patterns matched against a tool call's \`args.path\`; matching outputs are NEVER pruned. Currently: ${protectedToolsDisplay(config.protectedPaths)}. Edit via \`/pruner protected-paths\` (interactive) or \`/pruner protected-paths <comma-separated globs>\`. Set to 'none' to disable (kill switch). Default protects skill files: **/skills/**/*.md`;
|
|
259
|
+
return `Glob patterns matched against a tool call's \`args.path\`; matching outputs are NEVER pruned. Currently: ${protectedToolsDisplay(config.protectedPaths)}. Edit via \`/pruner protected-paths\` (interactive) or \`/pruner protected-paths <comma-separated globs>\`. Set to 'none' to disable (kill switch). Default protects skill files and per-repo gauntlet overrides: **/skills/**/*.md, **/gauntlet-overrides.md`;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
const HELP_TEXT = `pruner — automatically summarizes tool-call outputs to keep context lean.
|
package/src/config.test.ts
CHANGED
|
@@ -35,6 +35,22 @@ async function writeContextPrune(overrides: Record<string, unknown>): Promise<vo
|
|
|
35
35
|
await writeFile(settingsPath(), JSON.stringify({ contextPrune: overrides }));
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
describe("loadConfig protectedPaths", () => {
|
|
39
|
+
it("uses the defaults when unset", async () => {
|
|
40
|
+
await writeContextPrune({});
|
|
41
|
+
const config = await loadConfig();
|
|
42
|
+
expect(config.protectedPaths).toEqual(DEFAULT_CONFIG.protectedPaths);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("replaces defaults with user-supplied paths, including an empty list", async () => {
|
|
46
|
+
for (const protectedPaths of [["**/custom.md"], []]) {
|
|
47
|
+
await writeContextPrune({ protectedPaths });
|
|
48
|
+
const config = await loadConfig();
|
|
49
|
+
expect(config.protectedPaths).toEqual(protectedPaths);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
38
54
|
describe("loadConfig recoveryGraceTurns normalization", () => {
|
|
39
55
|
it("preserves an explicit 0", async () => {
|
|
40
56
|
await writeContextPrune({ recoveryGraceTurns: 0 });
|
package/src/protected.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { globToRegExp, isProtected } from "./protected.js";
|
|
3
|
+
import { DEFAULT_CONFIG } from "./types.js";
|
|
3
4
|
|
|
4
5
|
describe("globToRegExp", () => {
|
|
5
6
|
test("** crosses path segments", () => {
|
|
@@ -33,6 +34,27 @@ describe("globToRegExp", () => {
|
|
|
33
34
|
});
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
describe("isProtected defaults", () => {
|
|
38
|
+
test.each([".pi/gauntlet-overrides.md", "doc/gauntlet-overrides.md"])(
|
|
39
|
+
"protects reads of %s",
|
|
40
|
+
(path) => {
|
|
41
|
+
expect(isProtected("read", { path }, DEFAULT_CONFIG)).toBe(true);
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
test("protects skill reads", () => {
|
|
46
|
+
expect(isProtected("read", { path: "/h/skills/x/SKILL.md" }, DEFAULT_CONFIG)).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("does not protect sibling settings reads", () => {
|
|
50
|
+
expect(isProtected("read", { path: ".pi/settings.json" }, DEFAULT_CONFIG)).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("does not infer paths from bash commands", () => {
|
|
54
|
+
expect(isProtected("bash", { command: "cat .pi/gauntlet-overrides.md" }, DEFAULT_CONFIG)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
36
58
|
describe("isProtected", () => {
|
|
37
59
|
const cfg = { protectedTools: ["todowrite"], protectedPaths: ["**/skills/**/*.md"] };
|
|
38
60
|
|
package/src/types.ts
CHANGED
|
@@ -352,7 +352,8 @@ export interface ContextPruneConfig {
|
|
|
352
352
|
/**
|
|
353
353
|
* Glob patterns matched against a tool call's `args.path`. Matching calls are
|
|
354
354
|
* protected with identical semantics to protectedTools. Default protects
|
|
355
|
-
* skill files and their sibling reference docs under any `skills/` dir
|
|
355
|
+
* skill files and their sibling reference docs under any `skills/` dir,
|
|
356
|
+
* plus per-repo `gauntlet-overrides.md` files.
|
|
356
357
|
* Kill switch: set to [] in settings.json (`contextPrune.protectedPaths`).
|
|
357
358
|
*/
|
|
358
359
|
protectedPaths: string[];
|
|
@@ -562,7 +563,7 @@ export const DEFAULT_CONFIG: ContextPruneConfig = {
|
|
|
562
563
|
summarizerIdleTimeoutMs: 20000,
|
|
563
564
|
summarizerMaxTimeoutMs: 180000,
|
|
564
565
|
protectedTools: [],
|
|
565
|
-
protectedPaths: ["**/skills/**/*.md"],
|
|
566
|
+
protectedPaths: ["**/skills/**/*.md", "**/gauntlet-overrides.md"],
|
|
566
567
|
chainCompression: {
|
|
567
568
|
enabled: true,
|
|
568
569
|
rollingWindow: 3,
|