pi-tuck 1.0.9 → 1.0.11

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 CHANGED
@@ -3,6 +3,84 @@
3
3
  All notable changes to pi-tuck. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [1.0.11] - 2026-11-06
7
+
8
+ ### Fixed
9
+
10
+ - A prompt that fits the room is no longer dropped when the previous summary alone fills it (the 1.0.10 known issue): a cut can now shrink a
11
+ summary that sits alone, when the cut is due. A prompt that fits beside the summary is sent as it stands.
12
+ - A fresh tool result is clipped in more of the cases where the request would otherwise be refused. The clip's budget is now counted in the
13
+ same units as the request and allows for what the smallest cut keeps, so a round that fits only when clipped no longer ends the run. This
14
+ is most of the refusals the settings stress test found.
15
+ - One wrong usage report no longer refuses later prompts: a reply that reports more than the model's window is not learned from.
16
+ - When a provider refuses a request as over its window, pi-tuck now says so in a notice, where the turn could read as a hang.
17
+ - Every refusal says why.
18
+
19
+ ### Added
20
+
21
+ - A settings stress test (`test/stress`, run with `STRESS=1`): real Pi with a scripted fake model, across 272 cells of window, reserve,
22
+ keep-recent, output cap, setup size and usage reports. CI runs it on Pi 1.2.0 and the newest Pi. Rejection wordings and usage reports in
23
+ it are scripted: no hosted provider API is tested.
24
+
25
+ ### Known issues
26
+
27
+ - Same settings stress test, this build: 4 of 272 cells report a failure (90 on 1.0.10), none stuck and none losing work. The next prompt runs.
28
+ - A fresh assistant message larger than the free room is refused: nothing can clip it yet. A short next prompt recovers. In an unattended
29
+ `pi -p` run, a refusal ends the run.
30
+ - With a setup near the band, cuts can come too often: with Pi's defaults, a 40k setup at a 65,536 window made 25 cuts in 60 tool rounds, each
31
+ freeing little. It costs extra work, not a task. One sampled cell at a 167,739 window also trips this count. The fix is planned for the next
32
+ patch.
33
+ - A wrong usage report at or under the window can still raise the estimate for a while: in the stress cell for it, a 6,000-token prompt is
34
+ refused in a 28,672-token room.
35
+ - If a provider keeps rejecting requests in a wording Pi does not class as a context overflow, or with a bare HTTP 413, pi-tuck does not recover
36
+ on its own: the provider's error reaches you, the session stays intact, and every prompt can fail until the request shrinks. Try `/compact`.
37
+
38
+ ### Release gates
39
+
40
+ - **Shipped without a live gate.** Its replay matches 1.0.10, so the paired live test has nothing to measure on recorded sessions; the fixes
41
+ act only where a request would otherwise be refused. For that reason replay cannot show them working either, and they are shown by the stress
42
+ test alone, on a scripted model.
43
+ - Passed: tests; replay of the exact code on 29 recordings against 1.0.10 (13,946 requests, 0 differ, 0 dead tasks, the same 9 clips, all read
44
+ back); the stress test (no cell stuck, none losing work; no cell has a failure 1.0.10 does not. One cell makes 8 cuts where 1.0.10 made 6,
45
+ because its prompts now run instead of being refused).
46
+ - An earlier 1.0.11 build ran Pi's defaults on chain t06 with no lost task
47
+ ([`benchmark/RESULTS-DEFAULTS-1010.md`](benchmark/RESULTS-DEFAULTS-1010.md)). One of its tasks, task 21, ran to the 30-minute wall cap on a
48
+ first request the local server never answered, and recorded no token. It is read as a server stall. It was counted as passing under the bar's
49
+ definition; the registered counter alone would count one dead task.
50
+
51
+ ## [1.0.10] - 2026-11-05
52
+
53
+ ### Fixed
54
+
55
+ - A session no longer stays stuck when Pi's `compaction.keepRecentTokens` covers the whole conversation (the 1.0.8 and 1.0.9 known issue,
56
+ at Pi's defaults on small windows). The cut a prompt needs is now pi-tuck's own: it is planned when the prompt arrives, as at a tool
57
+ boundary, every request of that run carries it, and the run's first boundary saves it. Pi's cut finder, which keeps the last
58
+ keepRecentTokens and then has nothing to cut, is no longer asked. A run that ends before a boundary drops the cut, and the next prompt
59
+ decides afresh.
60
+
61
+ ### Changed
62
+
63
+ - A prompt's cut no longer goes through Pi's `session_before_compact`, so another extension's veto there no longer stops it. It never
64
+ stopped a cut between tool rounds either. Pi's own threshold and overflow cuts, and `/compact`, still ask other extensions as before.
65
+
66
+ ### Known issue
67
+
68
+ - When the previous summary alone fills the room and a large prompt arrives, the prompt is dropped: a cut cannot yet shrink a summary on its
69
+ own. 1.0.10 drops it with no message, where 1.0.9 refused it with one ("Already compacted"). The next prompt runs normally. Seen once, on a
70
+ 25,160-character prompt at a 32k window. A fix is planned for 1.0.11.
71
+
72
+ ### Release gates
73
+
74
+ - Passed: tests; replay of the exact build on 29 recordings (no dead task, no refused request, every oversize result clipped and read
75
+ back); the Pi-defaults rerun on chain t06, which lost 28 of 30 tasks on 1.0.8: 0 dead tasks and 0 stuck sessions
76
+ ([`benchmark/RESULTS-DEFAULTS-1010.md`](benchmark/RESULTS-DEFAULTS-1010.md)). It ran on a slow local server: 27 of 30 tasks hit the 30-minute
77
+ wall cap, and 2 of them sat on a first request the server never answered and recorded no token. Those 2 are read as server stalls, and a strict
78
+ no-token count would not meet the bar.
79
+ One task was refused mid-run: its round carried 9,020 tokens of reasoning, so the next request was about 17,801 against the 16,384-token room.
80
+ - **Failed as registered:** the paired 32k gate against 1.0.9 (chains t01 and t02, 60 tasks). No task was lost in either arm, but 1.0.10
81
+ resolved 49 tasks against 50, so "resolved no lower" failed by one task (−1.7 points, one-sided 95% lower bound −8.3;
82
+ [`benchmark/RESULTS-V110.md`](benchmark/RESULTS-V110.md)). The user approved shipping as an exception because of the small-window defect.
83
+
6
84
  ## [1.0.9] - 2026-11-01
7
85
 
8
86
  ### Changed
@@ -18,6 +96,18 @@ All notable changes to pi-tuck. The format follows [Keep a Changelog](https://ke
18
96
  - `tuck_search` takes a result back as returned: each entry of `priorResults` is a source's `sourceSha` and `read`, unchanged. It asked for
19
97
  `start` and `end`, which no result carried.
20
98
 
99
+ ### Known issue
100
+
101
+ - Affects 1.0.8 and 1.0.9. At a small window, Pi's default `compaction.keepRecentTokens` (20,000) can exceed pi-tuck's input room (the window
102
+ less the larger of `compaction.reserveTokens` and the model's maximum output; 16,384 at a 32k window with the default reserve). After a
103
+ run that is stopped (Esc, or a time limit) with the context above the room, every later prompt can be refused with "The request needs a cut
104
+ and Pi has none to make (Nothing to compact (session too small))", and the session stays stuck. Workaround until 1.0.10: set
105
+ `"compaction": {"keepRecentTokens": N}` in Pi's `settings.json` (or `compaction.modelOverrides["provider/model"].keepRecentTokens` for one
106
+ model), with N well below the room. Windows of 128k and up are not affected at the defaults. 1.0.10 is planned to fix this: pi-tuck will
107
+ make prompt-time cuts itself. Evidence: in one check at these defaults (1.0.8, 32k window, one session of 30 tasks, one model), 28 tasks
108
+ were lost ([`benchmark/RESULTS-DEFAULTS.md`](benchmark/RESULTS-DEFAULTS.md)); it shows that the failure occurs, not how often.
109
+
110
+ ## [1.0.8] - 2026-11-01
21
111
 
22
112
  ### Fixed
23
113
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-tuck
2
2
 
3
- **Compaction for [Pi](https://pi.dev) with no model call, and a history the agent can search by keyword, and on Apple silicon by meaning, all on your machine.** In our live run, a cut took a median 0.09 s. It costs no tokens. The same session always gives the same summary. And nothing is lost: the agent can read back anything earlier, word for word, and on Apple silicon it can find it by meaning.
3
+ **Compaction for [Pi](https://pi.dev) with no model call, and a history the agent can search by keyword, and on Apple silicon by meaning, all on your machine.** In our 128k live run, a cut took a median 0.08 s (pi-tuck 1.0.1). It costs no tokens. The same session always gives the same summary. And nothing is lost: the agent can read back anything earlier, word for word, and on Apple silicon it can find it by meaning.
4
4
 
5
5
  ```sh
6
6
  pi install npm:pi-tuck
@@ -8,22 +8,29 @@ pi install npm:pi-tuck
8
8
 
9
9
  ## pi-tuck against Pi's built-in compaction
10
10
 
11
- Pi's built-in compaction asks a model to write a summary. pi-tuck builds the summary from the session itself. The two time and context rows compare pi-tuck 1.0.1 with Pi 1.4.0's built-in compaction, where a local model writes the summary. Each measured result in this README names the pi-tuck build or version it was measured on; the release you install may be newer.
11
+ Pi's built-in compaction asks a model to write a summary. pi-tuck builds the summary from the session itself. The time, context and task rows compare pi-tuck 1.0.1 with Pi 1.4.0's built-in compaction, where a local model writes the summary. Each measured result in this README names the pi-tuck build or version it was measured on; the release you install may be newer.
12
12
 
13
13
  | | Pi's built-in (local model writes the summary) | pi-tuck |
14
14
  |---|---|---|
15
15
  | Model call per cut | One, to a model you pay for or wait on | None |
16
- | Time per cut | **269 s** (median) | **0.09 s** (median) |
17
- | Context carried after a cut | 29,200 tokens | 27,800 tokens (about the same) |
16
+ | Time per cut | **235 s** (median) | **0.08 s** (median; an upper bound, see below) |
17
+ | Context carried after a cut | 27,750 tokens | 26,790 tokens (about the same) |
18
+ | Facts cut away, asked back at 128k (mechanical probes) | Answered from the summary alone, with no recall tool | Answered from the summary and pi-tuck's recall tools, cuts 1 to 4 only. Claim line below the table |
19
+ | Tasks resolved at 128k (300 SWE-bench Verified tasks per arm, pi-tuck 1.0.1) | 251 of 300 | 258 of 300. The registered claim is only "no loss larger than 0.4 points"; chain s09 matters, see below |
18
20
  | Same session, same summary | Not guaranteed, since a model writes it | Yes |
19
21
  | Can the agent get dropped detail back | No recall tool in Pi 1.4.0. The full history stays in the session file, but the agent has no tool made to search it | Yes. `tuck_recall` reads any original exactly |
20
22
  | Search history by meaning | No | Yes, with `tuck_search`, on macOS with Apple silicon. It runs locally on your Mac's GPU, and no text leaves your machine. Elsewhere there is no `tuck_search`, and `tuck_recall` searches by keyword |
21
23
  | Can the summary invent history | It can, since a model writes it | No, it holds only recorded text |
22
24
  | What the summary leaves out | Whatever the model dropped | Tool output only; the agent reads it back exactly |
23
- | Billed input at the default 60% cut point (simulated) | Not measured | 10–18% less than pi-tuck cutting at the full room. This is not a comparison with the built-in |
24
25
  | Tasks resolved at a 32k window (150 SWE-bench Verified tasks, one model, pi-tuck 1.0.5) | 94 of 150 | 120 of 150 (+17.3 points, 95% lower bound +10.7). Limits below the table |
25
26
 
26
- The time and context rows are live runs with a 131k window: 27 cuts per arm over 5 chains, interim. The 32k row is one model, one window and five chains. Observation masking, a third approach, resolved 117 of the same 150 tasks (pi-tuck +2.0 points, lower bound −2.7: no difference shown). In the 32k run only pi-tuck was held to the 16,384-token room: Pi's built-in compaction sent a request over it in 97 of 150 tasks. Every number comes from a file in this repository. Conditions, sample sizes and the full limits are in [Benchmarks](#benchmarks).
27
+ The time and context rows are from the 128k live run: 10 chains of 30 tasks, pi-tuck 1.0.1, medians over 46 pi-tuck cuts and 48 cuts by Pi's compaction. pi-tuck's cut time is an upper bound, read from the session file ([`benchmark/DEVIATIONS.md`](benchmark/DEVIATIONS.md)).
28
+
29
+ The retention row is a paired replay on mechanical probes only: files changed, test-runner summary, required tag, error class and the session's result-line rule. No probe judged how well an agent reasons. The claim line, as [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) words it: **pi-tuck, answering with its recall tools, retains probed facts at a rate not more than 3 points below that of "Pi's compaction prompts on mac-studio/main:xhigh" (20 recordings) or of "Pi's compaction prompts on GPT-6.1 Terra" (19 of 20 recordings, s09-A missing), each answering from its summary alone, on these probes.** This is not summary against summary: it compares pi-tuck's summary together with its recall tools against Pi's compaction without them, and the run has no pi-tuck arm without its recall tools. It says nothing about any other compactor, model or window.
30
+
31
+ In the replay behind pi-tuck's retention numbers, 52 of its 95 answer sets ended in an abort (0 of 95 for Pi's compaction prompts on the same local model), so those numbers cover cuts 1 to 4 only and no retention claim is made past cut 4; see [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md).
32
+
33
+ The 32k row is one model, one window and five chains. Observation masking, a third approach, resolved 117 of the same 150 tasks (pi-tuck +2.0 points, lower bound −2.7: no difference shown). In the 32k run only pi-tuck was held to the 16,384-token room: Pi's built-in compaction sent a request over it in 97 of 150 tasks. Every number comes from a file in this repository. Conditions, sample sizes and the full limits are in [Benchmarks](#benchmarks).
27
34
 
28
35
  ## Ask your history
29
36
 
@@ -57,8 +64,8 @@ Then start a new Pi session. That is all. pi-tuck takes over Pi's compaction, bo
57
64
 
58
65
  - Needs Pi 1.2.0 or later (tested on 1.2.0 and 1.4.0), on macOS or Linux. On an older Pi, pi-tuck stays off, Pi's own compaction runs, and pi-tuck says so once. Full features on macOS with Apple silicon; elsewhere, compaction and word recall.
59
66
  - On macOS with Apple silicon, the first session also downloads a local search model in the background (about 390 MB, once). Until it is ready, search works by keyword. See [Semantic search](#semantic-search).
60
- - Prefer GitHub? `pi install git:github.com/galyavin/pi-tuck@v1.0.9`.
61
- - To pin a version, add it: `pi install npm:pi-tuck@1.0.9`.
67
+ - Prefer GitHub? `pi install git:github.com/galyavin/pi-tuck@v1.0.10`.
68
+ - To pin a version, add it: `pi install npm:pi-tuck@1.0.10`.
62
69
 
63
70
  ## How it works
64
71
 
@@ -89,14 +96,14 @@ On Apple silicon, both tools cost about 1,050 tokens of tool description per req
89
96
 
90
97
  ## What is proven, and what is not
91
98
 
92
- **Measured so far.** In the live run at a 131k window, a pi-tuck cut took a median 0.09 s, where Pi's built-in compaction with a local model writing the summary took a median 269 s. A cut calls no model, so it costs no tokens. After a cut, pi-tuck carried about as many tokens as the built-in did, not fewer. In simulation, cutting at 60% of the room instead of 100% saved billed input for pi-tuck itself, and answers held in replay. At a 32k window, in one suite of 150 SWE-bench Verified tasks with one model, pi-tuck 1.0.5 resolved 120 tasks and Pi's built-in compaction 94 (+17.3 points, 95% lower bound +10.7). Observation masking resolved 117 (pi-tuck +2.0 points, lower bound −2.7: no difference shown). The limits of that result are in [Benchmarks](#benchmarks).
99
+ **Measured so far.** In the 128k live run (pi-tuck 1.0.1), a pi-tuck cut took a median 0.08 s, where Pi's compaction with a local model writing the summary took a median 235 s. A cut calls no model, so it costs no tokens. After a cut, both carried about the same context. On mechanical probes about facts cut away, pi-tuck with its recall tools was not more than 3 points below Pi's compaction prompts on mac-studio/main:xhigh, or below Pi's compaction prompts on GPT-6.1 Terra, each answering from its summary alone (claim line above; cuts 1 to 4 only). Over 300 tasks per arm, pi-tuck resolved 258 and Pi's compaction 251; the registered claim is only that pi-tuck lost no more than 0.4 points. In replay, answers held when pi-tuck cut at 60% of the room instead of 100%. At a 32k window, in one suite of 150 SWE-bench Verified tasks with one model, pi-tuck 1.0.5 resolved 120 tasks and Pi's built-in compaction 94 (+17.3 points, 95% lower bound +10.7). Observation masking resolved 117 (pi-tuck +2.0 points, lower bound −2.7: no difference shown). The limits of each result are in [Benchmarks](#benchmarks).
93
100
 
94
- **Not claimed.** This README does not say that pi-tuck is as good as, or better than, Pi's built-in compaction at a 128k window or in any other setting than the one above. The tests were pre-registered before any result came in. The plans, and every result, including those that did not favour pi-tuck, are in [`benchmark/`](benchmark).
101
+ **Not claimed.** This README does not say that pi-tuck is as good as or better than any compactor, model, window or kind of task other than those named, and it never calls it "the best LLM compactor". The retention tests compare pi-tuck with its recall tools against Pi's compaction without them. The tests were pre-registered before any result came in. The plans, and every result, including those that did not favour pi-tuck, are in [`benchmark/`](benchmark).
95
102
 
96
103
  The pre-registered plan has three parts:
97
104
 
98
105
  1. **A live run** at a 128k window on a local model, with pi-tuck 1.0.1. Ten chains of 30 SWE-bench Verified tasks per arm, with Pi's built-in compaction in one arm and pi-tuck in the other. Official grading.
99
- 2. **A paired replay with probe questions.** The same cuts, made by pi-tuck and by Pi's summarize prompts, then questions about facts that fell before the cut. Pre-registered rule: the claim holds only if pi-tuck is no more than 3 points worse than the better compactor, with a 95% bound. The comparators are named in [`benchmark/PINS.md`](benchmark/PINS.md).
106
+ 2. **A paired replay with probe questions.** The same cuts, made by pi-tuck and by Pi's summarize prompts, then questions about facts that fell before the cut. Pre-registered rule: the claim holds only if pi-tuck is no more than 3 points worse than the better compactor, with a one-sided 95% bound. The comparators are named in [`benchmark/PINS.md`](benchmark/PINS.md) and above.
100
107
  3. **A 32k suite**, where tasks are bigger than the room, so cuts must summarise work in progress. Its result is the 32k row above.
101
108
 
102
109
  The plan: [`benchmark/PREREG.md`](benchmark/PREREG.md), [`benchmark/PREREG-32K.md`](benchmark/PREREG-32K.md), [`benchmark/PREREG-TERRA.md`](benchmark/PREREG-TERRA.md), and every change after the freeze in [`benchmark/DEVIATIONS.md`](benchmark/DEVIATIONS.md).
@@ -104,20 +111,29 @@ The plan: [`benchmark/PREREG.md`](benchmark/PREREG.md), [`benchmark/PREREG-32K.m
104
111
  Known limits today:
105
112
 
106
113
  - pi-tuck's summary does not explain why things happened. It holds the words that were said, not a model's reading of them. The agent must look things up. Each look-up costs a tool call.
107
- - Small windows: an upstream that spends far more context on tool schemas than others (some OpenRouter upstreams do) can leave a small input room too little space, and pi-tuck then refuses to cut rather than overflow. This is what made 1.0.1 refuse at a 32k window with Pi's default 16,384 reserve. Since 1.0.4 the notice says when the provider is counting far above pi-tuck's estimate. In 1.0.4, after an oversize tool result or a "Nothing to compact" error from Pi, a session could keep refusing later prompts. 1.0.5 fixes both. Since 1.0.8, a fresh tool result too large for the room is clipped (its head and tail, with a `tuck_recall` locator to the rest) instead of refusing the run. Pin a different upstream, lower the reserve, or use a larger window; see [Small windows](#small-windows).
114
+ - Small windows: an upstream that spends far more context on tool schemas than others (some OpenRouter upstreams do) can leave a small input room too little space. pi-tuck then refuses to cut rather than overflow. This made 1.0.1 refuse at a 32k window with Pi's default 16,384 reserve. Version 1.0.4 added a notice that says when the provider counts far above pi-tuck's estimate. In 1.0.4, after an oversize tool result or a "Nothing to compact" error from Pi, a session could keep refusing later prompts; 1.0.5 fixed both. Version 1.0.8 clips a fresh tool result that is too large for the room (its head and tail, with a `tuck_recall` locator to the rest) instead of refusing the run. If a small window still refuses, pin a different upstream, lower the reserve, or use a larger window; see [Small windows](#small-windows).
115
+ - Small window, after a stopped run (1.0.8 and 1.0.9 only; fixed in 1.0.10): Pi's default `compaction.keepRecentTokens` (20,000) can exceed pi-tuck's input room, which is the window less the larger of `compaction.reserveTokens` and the model's maximum output (16,384 at a 32k window with the default reserve). After a run that is stopped (Esc, or a time limit) with the context above the room, every later prompt could be refused with "The request needs a cut and Pi has none to make (Nothing to compact (session too small))", and the session stayed stuck. In one check at these defaults (pi-tuck 1.0.8, 32k window, one session of 30 tasks, one local model), 28 tasks were lost, tasks 3 to 30 ([`benchmark/RESULTS-DEFAULTS.md`](benchmark/RESULTS-DEFAULTS.md)). If you are on 1.0.8 or 1.0.9, upgrade to 1.0.10. In 1.0.10 the cut a prompt needs is pi-tuck's own, so `keepRecentTokens` no longer leaves it with nothing to cut. A rerun of the same check on the 1.0.10 candidate lost no task and got stuck nowhere, with the caveat that two of its tasks had a first request the local server never answered, which a literal no-token count would call lost ([`benchmark/RESULTS-DEFAULTS-1010.md`](benchmark/RESULTS-DEFAULTS-1010.md)). Windows of 128k and up were not affected at the defaults.
116
+ - Small window, a large prompt after a summary-only cut (1.0.10): when the previous summary alone fills the room and a large prompt arrives, the prompt is dropped, because a cut cannot yet shrink a summary on its own. 1.0.10 drops it with no message, where 1.0.9 refused it with one. The next prompt runs normally. Seen once, on a 25,160-character prompt at a 32k window ([`benchmark/RESULTS-CUTLATER.md`](benchmark/RESULTS-CUTLATER.md), t03 task 24). A fix is planned for 1.0.11.
117
+ - Other extensions (1.0.10): a prompt's cut no longer goes through Pi's `session_before_compact`, so another extension's veto there no longer stops it. It never stopped a cut between tool rounds. Pi's own threshold and overflow cuts, and `/compact`, still ask other extensions as before.
108
118
  - Semantic search (`tuck_search`) needs a Mac with Metal (Apple silicon). Elsewhere, compaction and `tuck_recall`'s word search work, there is no `tuck_search`, and nothing is downloaded.
109
119
 
110
120
  ## Benchmarks
111
121
 
112
- Each row says what was measured, under which conditions, and where the data is. "Interim" means the run is not finished.
122
+ Each row says what was measured, under which conditions, and where the data is.
113
123
 
114
124
  | What | Result | Conditions | Source |
115
125
  |---|---|---|---|
116
- | Tasks resolved, 32k window | Pi's built-in compaction 94 of 150; pi-tuck 120 of 150 (+17.3 points, 95% lower bound +10.7); observation masking 117 of 150 (pi-tuck +2.0, lower bound −2.7: no difference shown). Turns per task: 9.4 (built-in), 24.0 (pi-tuck), 11.9 (masking). Tokens per session: 6.10 M, 6.39 M, 7.02 M. Spend per arm for the five sessions: $0.87, $0.55, $0.94 | pi-tuck 1.0.5 (commit `aa30824`, the 1.0.5 code; 1.0.6 and 1.0.7 are not measured). DeepSeek V4.1 Flash pinned to one upstream, window 32,768, reserve 16,384, 5 chains of 30 SWE-bench Verified tasks, paired by task, one-sided bootstrap over tasks. **Limits:** only pi-tuck is held to the 16,384-token room (the built-in sent a request over it in 97 of 150 tasks, masking in 73, so the window is nominal for them). The difference by chain is +10.0, −3.3, +50.0, +16.7 and +13.3 points; leaving out chain t03 it is +9.2. The bootstrap resamples tasks, not chains. The pi-tuck arm ran at a different time from the others. One model, one window. pi-tuck refused to cut mid-run in 5 tasks, where one fresh tool result was larger than the room; they count as unresolved. In the same setup pi-tuck 1.0.3 resolved 72 of 150, with 52 tasks lost because an earlier refusal carried forward; 1.0.5 fixes that | [`benchmark/RESULTS-32K-ACCEPT.md`](benchmark/RESULTS-32K-ACCEPT.md) |
117
- | The 1.0.8 candidate against 1.0.7, 32k window | 0 dead tasks in both arms. Resolved 46 of 60 in both (+0.0 points, 95% lower bound −6.7). No mid-run refusal in either arm. Turns per task 25.37 (1.0.7) and 26.10 (1.0.8) | Pre-registered gate, passed as registered. pi-tuck `a2e7d1a` (the tool-result clip and the cut at the end of a run, on 1.0.6) against 1.0.7. Released 1.0.8 adds 1.0.7's Metal-only recall to that build. DeepSeek V4.1 Flash pinned to one upstream, window 32,768, reserve 16,384, chains t01 and t02, 60 paired tasks, both arms run together. One model, one window, one run per arm; 60 tasks cannot show a difference smaller than the bound. The run shows that the change introduced no refusal; no refusal occurred in the 1.0.7 arm for the clip to prevent | [`benchmark/RESULTS-V108.md`](benchmark/RESULTS-V108.md) |
118
- | Cut time, live | pi-tuck median 0.087 s (90th percentile 0.205 s); Pi's built-in median 268.8 s (90th percentile 421.6 s). Context carried after a cut, median: 27,751 against 29,241 tokens | **Interim.** pi-tuck 1.0.1, Pi 1.4.0, SWE-bench Verified chains of 30 tasks, 131,072-token window, reply reserve 32,768, local model `mac-studio/main` writing Pi's summaries. 27 cuts per arm, 5 chains. The built-in time depends on that model and on server load. pi-tuck's time is an upper bound read from the session file | [`benchmark/RESULTS-INTERIM.md`](benchmark/RESULTS-INTERIM.md) section 1 |
119
- | Cut time, paired replay | pi-tuck median 0.062 s (range 0.005 to 0.48 s); local-model summarizer 193.4 s (77.3 to 413.3 s); frontier summarizer 96.1 s (39.1 to 267.8 s; measured on the retired model `openai/gpt-6-astra`; feeds no test). Context carried after a cut, median: 28,006 (pi-tuck), 26,424 (local), 25,555 (frontier) tokens | **Interim.** The same recorded sessions cut at the same positions. pi-tuck: 10 recordings, 52 cuts. Summarizers: 6 recordings, 28 and 29 cuts. pi-tuck's time leaves out the background search index | [`benchmark/RESULTS-INTERIM.md`](benchmark/RESULTS-INTERIM.md) section 2 |
120
- | Billed input, cutting at 60% of the room instead of 100% | −17.9% at 128k, −14.0% at 32k with automatic prompt caching; −17.0% and −9.6% with explicit cache breakpoints. Cuts go from 6 to 12 (128k) and from 84 to 157 (32k); none frees under a tenth of the room | Simulated billing, not provider bills. Prompt caching priced. Build `1bda64b` (the default shipped in 1.0.3). 5 recorded live sessions. 128k = 131,072 window, reserve 32,768. 32k = 32,768 window, reserve 16,384 | [`rnd/replay32/RESULT-lean.md`](rnd/replay32/RESULT-lean.md) |
126
+ | Pi's default compaction settings, 32k window, pi-tuck 1.0.10 candidate | Passed as registered: 0 dead tasks and 0 stuck sessions on 30 tasks, on a slow local server (27 of 30 tasks hit the 30-minute wall cap, and 2 had a first request the server never answered). 6 resolved. A counter written later, which counts a task with no token as dead, would count those 2 | Candidate `a67ec30` of 1.0.10, `mac-studio/main`, window 32,768, output cap 16,384, Pi's default `reserveTokens` 16,384 and `keepRecentTokens` 20,000, one session of chain t06. One mid-run refusal (a round with 9,020 tokens of reasoning against the 16,384-token room). One session, one model; a pass shows no lost task in these 30 tasks, not that none can occur | [`benchmark/RESULTS-DEFAULTS-1010.md`](benchmark/RESULTS-DEFAULTS-1010.md) |
127
+ | Pi's default compaction settings, 32k window, pi-tuck 1.0.8 | 28 of 30 tasks lost (tasks 3 to 30, each refused at its prompt); 1 resolved. Fails the pre-registered bar of 0 lost tasks | pi-tuck 1.0.8 (`86935e9`), `mac-studio/main`, window 32,768, output cap 16,384, `reserveTokens` 16,384 and `keepRecentTokens` 20,000 (larger than the room), one session of chain t06. One session, one model, one build: it shows that the failure occurs at these settings, not how often. Not comparable with other rows | [`benchmark/RESULTS-DEFAULTS.md`](benchmark/RESULTS-DEFAULTS.md) |
128
+ | Tasks resolved, 32k window | Pi's built-in compaction 94 of 150; pi-tuck 120 of 150 (+17.3 points, 95% lower bound +10.7); observation masking 117 of 150 (pi-tuck +2.0, lower bound −2.7: no difference shown). Turns per task: 9.4 (built-in), 24.0 (pi-tuck), 11.9 (masking). Tokens per session: 6.10 M, 6.39 M, 7.02 M. Spend per arm for the five sessions: $0.87, $0.55, $0.94 | pi-tuck 1.0.5 (commit `aa30824`, the 1.0.5 code; 1.0.6 and 1.0.7 are not measured). DeepSeek V4.1 Flash pinned to one upstream, window 32,768, reserve 16,384, 5 chains of 30 SWE-bench Verified tasks, paired by task, one-sided bootstrap over tasks. **Limits:** only pi-tuck is held to the 16,384-token room (the built-in sent a request over it in 97 of 150 tasks, masking in 73, so the window is nominal for them). The difference by chain is +10.0, −3.3, +50.0, +16.7 and +13.3 points; leaving out chain t03 it is +9.2. The bootstrap resamples tasks, not chains. The pi-tuck arm ran at a different time from the others. One model, one window. pi-tuck refused to cut mid-run in 5 tasks, where one fresh tool result was larger than the room; they count as unresolved. In the same setup pi-tuck 1.0.3 resolved 72 of 150, with 52 dead tasks (tasks refused at their prompt after an earlier failure). In the pi-tuck arm of this row there was 1 dead task (t03 task 24: refused at its prompt with "Nothing to compact"; the next task ran normally), so this build missed the pre-registered acceptance bar of zero dead tasks by one task (the other part of the bar, no loss against 1.0.3 by more than 3 points, was met) | [`benchmark/RESULTS-32K-ACCEPT.md`](benchmark/RESULTS-32K-ACCEPT.md) |
129
+ | The 1.0.10 candidate against 1.0.9, 32k window | 0 dead tasks in both arms; no mid-run refusal. Resolved 50 of 60 (1.0.9) and 49 of 60 (1.0.10 candidate): −1.7 points, 95% lower bound −8.3. **The gate failed as registered, by one task**; 1.0.10 shipped as an exception because of the small-window defect. Turns per task 25.08 and 29.12; cuts 731 and 884; total tokens 13.07 M and 15.12 M | Pre-registered gate. pi-tuck `a67ec30` against 1.0.9 (`cd4626b`). DeepSeek V4.1 Flash pinned to one upstream, window 32,768, reserve 16,384, `keepRecentTokens` 3,333, chains t01 and t02, 60 paired tasks. One model, one window, one run per arm; 60 tasks cannot separate a one-task difference from run-to-run variation | [`benchmark/RESULTS-V110.md`](benchmark/RESULTS-V110.md) |
130
+ | The 1.0.8 candidate against 1.0.7, 32k window | 0 dead tasks in both arms. Resolved 46 of 60 in both (+0.0 points, 95% lower bound −6.7). No mid-run refusal in either arm. Turns per task 25.37 (1.0.7) and 26.10 (1.0.8) | Pre-registered gate, passed as registered. pi-tuck `a2e7d1a` (the tool-result clip and the cut at the end of a run, on 1.0.6) against 1.0.7. Released 1.0.8 is that build merged with 1.0.7's Metal-only recall; its replay requests are reported byte-identical to the gated build on 29 recordings (stated in the source file, not re-run for this README). DeepSeek V4.1 Flash pinned to one upstream, window 32,768, reserve 16,384, chains t01 and t02, 60 paired tasks, both arms run together. One model, one window, one run per arm; 60 tasks cannot show a difference smaller than the bound. The run shows that the change introduced no refusal; no refusal occurred in the 1.0.7 arm for the clip to prevent | [`benchmark/RESULTS-V108.md`](benchmark/RESULTS-V108.md) |
131
+ | Cut time, live, 128k | pi-tuck median 0.081 s (46 cuts; 90th percentile 0.279 s). Pi's compaction median 235.3 s (48 cuts; 90th percentile 403.8 s). Context carried after a cut, median: 26,790 tokens (pi-tuck), 27,750 (Pi's compaction) | pi-tuck 1.0.1, Pi 1.4.0, SWE-bench Verified, 10 chains of 30 tasks, window 128,000, reply reserve 32,768, `mac-studio/main:xhigh` writing Pi's summaries. The built-in time depends on that model and on server load. pi-tuck's time is an upper bound, read from the session file: the time between the entry written just before the cut and the compaction entry, so it includes anything between the last message and the cut (pi-tuck's tool-loop cuts emit no compaction events). The 44 failed compaction attempts in chain s09-A have no compaction entry and are not in the counts | [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) test 3 |
132
+ | Cut time, paired replay, 128k | pi-tuck median 0.061 s (95 cuts). Pi's compaction prompts on mac-studio/main:xhigh 154.6 s (86 cuts). Pi's compaction prompts on GPT-6.1 Terra 53.2 s (87 cuts, 19 recordings). Context carried after a cut, median: 27,656, 25,790 and 22,784 tokens; median summary 85,561, 23,598 and 11,200 characters | The same recorded sessions cut at the same raw positions. pi-tuck 1.0.1. Terra's cuts ran through Codex, not on the local server | [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) test 3 |
133
+ | Retention, 128k, test 1a: pi-tuck with its recall tools against Pi's compaction prompts on mac-studio/main:xhigh | Registered claim: not more than 3 points below, on these probes (cuts 1 to 4 only) (supported). Accuracy on the probes each was asked: 89.7% (783 probes) and 61.8% (1,677). Difference over the 783 shared probes, which come from the 43 of pi-tuck's 95 answer sets that did not abort: +25.7 points, one-sided 95% lower bound +22.0 (+22.3 clustered by chain). By probe type, pi-tuck / comparator: error class 80.2 / 24.7, files changed 84.5 / 78.9, result-line rule 100.0 / 100.0, required tag 100.0 / 99.3, test-runner summary 90.1 / 31.6 | pi-tuck 1.0.1, 20 recordings, mechanical probes only, evidence at least 35,000 raw tokens before the cut. pi-tuck answered with its recall tools and the comparators with none, from the summary and the kept recent messages. Every one of the 43 asks behind the 783 probes made recall calls (1.25 per probe on average), so no pi-tuck answer lacked the tools; the run cannot show how much of the result comes from the summary and how much from recall. No pi-tuck arm without recall tools exists. The comparators' agents had unrestricted bash, and the logs cannot rule out that one read a recording; nothing shows it. pi-tuck's accuracy does not rise with more calls per probe (87.9%, 90.8%, 90.0% by thirds; post hoc, descriptive). The 783 shared probes are fewer than the 2,969 needed to certify a difference near the margin | [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) test 1a |
134
+ | Retention, 128k, test 1b-Terra: pi-tuck with its recall tools against Pi's compaction prompts on GPT-6.1 Terra | Registered claim: not more than 3 points below, on these probes (cuts 1 to 4 only) (supported). Accuracy on the probes each was asked: 89.7% (783) and 57.8% (1,685). Difference over the 728 shared probes, from the same 43 of pi-tuck's 95 answer sets: +33.7 points, one-sided 95% lower bound +30.4 (+30.1 clustered by chain). By probe type, pi-tuck / Terra: error class 80.2 / 14.7, files changed 84.5 / 72.3, result-line rule 100.0 / 100.0, required tag 100.0 / 93.3, test-runner summary 90.1 / 38.1 | As test 1a, on 19 of the 20 recordings: s09-A is missing because its Terra cut job hit a Codex usage limit, and the set was closed at 19 before any Terra result was read. The claim is non-inferiority. The same limits as test 1a apply | [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) test 1b-Terra |
135
+ | Tasks resolved, 128k, test 2 | Pi's compaction 251 of 300, pi-tuck 258 of 300. Registered claim: no loss larger than 0.4 points. All 10 chains: +2.3 points, lower bound +0.3. Without chain s09: +1.5 points, lower bound −0.4 | pi-tuck 1.0.1, 10 chains of 30 tasks, paired by task, bootstrap over chains. In s09-A Pi's compaction failed 44 times ("generation hit the token cap") from task 23 on, and no other chain has a failed compaction; that is why the bound changes without s09 | [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) test 2 |
136
+ | Tokens per session, input plus output without cache reads, 128k | 241,000 fewer for pi-tuck per session on average, one-sided 95% upper bound −21,000, with all 10 chains. Without chain s09 the mean is −174,000 and the upper bound +43,000 | pi-tuck 1.0.1, paired by chain, bootstrap over sessions. The line holds only with all chains | [`benchmark/RESULTS-MAIN.md`](benchmark/RESULTS-MAIN.md) test 3 |
121
137
  | Answers after cutting at 60% instead of 100% | No more losses than the limit on all 4 sets. Losses / wins over n questions: 128k shared 3 / 7 of 99; 128k asymmetric 3 / 1 of 67; 32k shared 5 / 3 of 77; 32k asymmetric 9 / 5 of 343. In the 32k asymmetric set the losses lean against 60% (test_result 4 / 2, error_hit 3 / 1); it is the set to watch | Paired replay, same 5 sessions, DeepSeek flash as agent and judge. Limit, set in advance: losses minus wins at most max(2, 5% of n). Asymmetric = evidence the base keeps native and the variant has summarised | [`rnd/replay32/RESULT-lean.md`](rnd/replay32/RESULT-lean.md) |
122
138
  | 1.0.2 changes (plain-text summary, shrinking dialogue view, time on user turns only) against 1.0.1 | No more losses than the limit on all 4 sets. Losses / wins: 128k 1 / 2 of 128; 128k time questions 0 / 0 of 48; 32k 4 / 2 of 127; 32k asymmetric 0 / 2 of 56 | Paired replay, 5 recorded live sessions, DeepSeek flash as agent and judge. 11 of 146 answer sessions hit a 15-minute wall limit and drop out of the pairing | [`rnd/replay32/RESULT-lean.md`](rnd/replay32/RESULT-lean.md), [`CHANGELOG.md`](CHANGELOG.md) |
123
139
  | Summary size, plain-text format against the 1.0.1 JSON format | About 30% shorter where the agent writes little, about 14% shorter where it writes a lot | Same dialogue | [`CHANGELOG.md`](CHANGELOG.md) 1.0.2 |
@@ -126,9 +142,8 @@ Each row says what was measured, under which conditions, and where the data is.
126
142
 
127
143
  Notes:
128
144
 
129
- - The billing figures come from a simulation that replays real sessions through the real cut trigger and prices every request. They are not bills.
130
145
  - The answer replays use one cheap hosted model (DeepSeek flash) as agent and judge. They compare pi-tuck with other pi-tuck settings and versions. They do not compare pi-tuck with an LLM compactor.
131
- - The live and replay time rows measure pi-tuck 1.0.1. The newer versions are listed in the [changelog](CHANGELOG.md). A cut still calls no model, but the eval has not yet re-timed 1.0.3.
146
+ - The 128k rows measure pi-tuck 1.0.1. The newer versions are listed in the [changelog](CHANGELOG.md). A cut still calls no model, but the 128k run has not been repeated on them.
132
147
  - Everything here is from SWE-bench-style tasks on a small set of models. It may not carry to your work.
133
148
 
134
149
  ## Settings
@@ -139,7 +154,7 @@ pi-tuck needs none. Two are optional. Put them in `pi-tuck.json` in Pi's agent f
139
154
  { "cutAt": 0.6, "semanticRecall": true }
140
155
  ```
141
156
 
142
- - **`cutAt`** (above 0, at most 1; default 0.6) is the part of the input room a request may fill before pi-tuck cuts. At 0.6, each cut leaves room to grow, so fewer tokens are carried and billed per request. `{ "cutAt": 1 }` cuts at the full room. A lower value cuts earlier and smaller. The whole room still decides which request pi-tuck refuses.
157
+ - **`cutAt`** (above 0, at most 1; default 0.6) is the part of the input room a request may fill before pi-tuck cuts. At 0.6, each cut leaves room to grow. `{ "cutAt": 1 }` cuts at the full room. A lower value cuts earlier and smaller. The whole room still decides which request pi-tuck refuses.
143
158
  - **`semanticRecall`** (`false` turns semantic search off, on Apple silicon). Nothing is then downloaded, and `tuck_search` uses keywords only.
144
159
 
145
160
  A bad file is reported at session start, and pi-tuck uses the defaults.
@@ -1 +1 @@
1
- export declare const BUILD_ID = "c4e6e6034a54ff7a893ee62484e54cf471964be3435aa587af8a288dcbf41314";
1
+ export declare const BUILD_ID = "828ac4821689607af006d54a41fd5960bb910bc65aa43adbd81c2f6aac25d180";
@@ -1 +1 @@
1
- export const BUILD_ID = "c4e6e6034a54ff7a893ee62484e54cf471964be3435aa587af8a288dcbf41314";
1
+ export const BUILD_ID = "828ac4821689607af006d54a41fd5960bb910bc65aa43adbd81c2f6aac25d180";
@@ -4,6 +4,14 @@ export interface Clip {
4
4
  toolCallId: string;
5
5
  content: ToolResultMessage["content"];
6
6
  }
7
+ /** The budgets to offer a fresh round the room cannot hold whole, in the units the round and the mandatory input are counted in, tried in order.
8
+ * The room is the provider's, so it is divided by the factor the provider's counts have been running at - the same conversion the planner's own
9
+ * reserve makes (budget.ts) - before the input no cut can remove is taken out; counting the provider's room against an estimate asks the round for
10
+ * more than the room holds at a factor above 1. The first try is half of what that leaves. `kept` is what the smallest cut leaves the request
11
+ * besides the fresh results - the summary floor and its quote of the prompt - measured from the planner's own preview, not a new formula: a half
12
+ * cannot spend it, so the second try is the whole free room less it. Omit it when the planner bailed before building a preview, and only the half
13
+ * is tried. Nothing is offered when the mandatory input alone leaves no room. */
14
+ export declare const clipBudgets: (room: number, factor: number, mandatory: number, kept?: number) => number[];
7
15
  /** The round's text results within `budget` tokens together: each result smaller than an equal share of what is left stays whole, and the
8
16
  * rest are clipped to that share, keeping the most head and tail characters that fit it. None when the round already fits, or when a share
9
17
  * cannot hold even the locator. A round of one result gets the whole budget. Deterministic: the same round and budget always clip the same way. */
package/dist/src/clip.js CHANGED
@@ -9,6 +9,19 @@ const clipped = (text, keep, entryId) => {
9
9
  `\n[pi-tuck: characters ${head}-${text.length - tail} of ${text.length} are not shown, to fit the context window; read them with ` +
10
10
  `tuck_recall {"entryId":"${entryId}","offset":${head}}, up to ${MAX_RECALL_CHARACTERS} characters a call]\n` + text.slice(text.length - tail) }];
11
11
  };
12
+ /** The budgets to offer a fresh round the room cannot hold whole, in the units the round and the mandatory input are counted in, tried in order.
13
+ * The room is the provider's, so it is divided by the factor the provider's counts have been running at - the same conversion the planner's own
14
+ * reserve makes (budget.ts) - before the input no cut can remove is taken out; counting the provider's room against an estimate asks the round for
15
+ * more than the room holds at a factor above 1. The first try is half of what that leaves. `kept` is what the smallest cut leaves the request
16
+ * besides the fresh results - the summary floor and its quote of the prompt - measured from the planner's own preview, not a new formula: a half
17
+ * cannot spend it, so the second try is the whole free room less it. Omit it when the planner bailed before building a preview, and only the half
18
+ * is tried. Nothing is offered when the mandatory input alone leaves no room. */
19
+ export const clipBudgets = (room, factor, mandatory, kept) => {
20
+ const free = Math.floor(room / Math.max(1, factor)) - mandatory;
21
+ if (free <= 0)
22
+ return [];
23
+ return [...new Set(kept === undefined ? [Math.floor(free / 2)] : [Math.floor(free / 2), free - kept - 1])].filter(budget => budget > 0);
24
+ };
12
25
  /** The round's text results within `budget` tokens together: each result smaller than an equal share of what is left stays whole, and the
13
26
  * rest are clipped to that share, keeping the most head and tail characters that fit it. None when the round already fits, or when a share
14
27
  * cannot hold even the locator. A round of one result gets the whole budget. Deterministic: the same round and budget always clip the same way. */
@@ -6,6 +6,9 @@ import type { RecallPolicy } from "./recall.js";
6
6
  /** What a capacity refusal tells the operator. When the provider has counted far more input than pi-tuck's estimate, the refusal most
7
7
  * likely comes from that counting (an upstream behind a router can spend much more context on tool schemas), so the notice says so. */
8
8
  export declare const refusalNotice: (message: string, factor: number) => string;
9
+ /** What the operator hears when the provider refuses a request pi-tuck admitted as over its window: the bound that reply puts on the counting, and
10
+ * what pi-tuck did with it. A bound pi-tuck cannot carry (above the ceiling) says so rather than implying the next request is planned at it. */
11
+ export declare const overflowNotice: (contextWindow: number, bound: number, learned: boolean) => string;
9
12
  /** The most a seed or an overflow raise may set the calibration factor to. A paired reply's own ratio is not bounded by it. */
10
13
  export declare const CALIBRATION_CEILING = 1.5;
11
14
  /** How many recent paired replies (and overflow bounds, and the seed while it is among them) the calibration factor is the maximum of. */