pi-tuck 1.0.19 → 1.0.20
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 +22 -0
- package/README.md +5 -4
- package/dist/src/build-id.d.ts +1 -1
- package/dist/src/build-id.js +1 -1
- package/dist/src/compaction.d.ts +3 -3
- package/dist/src/compaction.js +9 -7
- package/dist/src/diagnostics.d.ts +1 -1
- package/package.json +1 -1
- package/src/build-id.ts +1 -1
- package/src/compaction.ts +13 -11
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
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.20] - 2026-11-09
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **pi-tuck cut earlier than native Pi when a model's reply cap is above Pi's reserve.** It reserved the larger of Pi's `compaction.reserveTokens`
|
|
11
|
+
and the model's `maxTokens`. With `maxTokens` 32,768 and a reserve of 16,384 on an 81,920 window, its room fell to 49,152 tokens, where native Pi
|
|
12
|
+
compacts at 65,536. pi-tuck now reserves Pi's `reserveTokens`, native's point. Once a reply on the current branch did not fit, it holds the larger
|
|
13
|
+
of the two for the rest of that branch: a reply stopped for length, or a request pi-ai classes as a context overflow. A transient error such as a
|
|
14
|
+
503 keeps native's room. This is read from the branch at each decision, so resume, fork and `/tree` follow the branch, and a new session starts
|
|
15
|
+
at `reserveTokens` again. A host that passes its own reserve keeps today's behaviour.
|
|
16
|
+
- Lowering `reserveTokens` now takes effect even when the model's `maxTokens` is larger, until a reply does not fit.
|
|
17
|
+
|
|
18
|
+
### Release gates
|
|
19
|
+
|
|
20
|
+
- **Shipped without a live gate, because its replay matches 1.0.19:** 13,946 requests on 29 recordings, 0 differ, 0 lost and 0 dead tasks, the same
|
|
21
|
+
17 clips. Every recording has the reply cap equal to the reserve, where nothing changes.
|
|
22
|
+
- New stress cells with the reply cap above the reserve, at 81,920 and 16,384, on a provider that clips the reply at the window end and one that
|
|
23
|
+
refuses prompt plus cap over the window, plus one with a transient 503. On Pi 1.2.0 and 1.5.0, 1.0.19 refuses in the four cap cells. This version
|
|
24
|
+
takes one clipped or refused reply in each and recovers, with no lost work and no compaction loop; after the 503 it keeps native's room. The full
|
|
25
|
+
stress test (292 cells) has 3 findings on each Pi, all also found on 1.0.19, and no cell is worse. Tests (676) and CI.
|
|
26
|
+
- Sessions that are open keep the build they started with until they are restarted.
|
|
27
|
+
|
|
6
28
|
## [1.0.19] - 2026-11-09
|
|
7
29
|
|
|
8
30
|
### Added
|
package/README.md
CHANGED
|
@@ -63,9 +63,9 @@ pi install npm:pi-tuck
|
|
|
63
63
|
Then start a new Pi session. That is all. pi-tuck takes over Pi's compaction, both automatic and `/compact`. You change no setting.
|
|
64
64
|
|
|
65
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.
|
|
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).
|
|
67
|
-
- Prefer GitHub? `pi install git:github.com/galyavin/pi-tuck@v1.0.
|
|
68
|
-
- To pin a version, add it: `pi install npm:pi-tuck@1.0.
|
|
66
|
+
- On macOS with Apple silicon, the first session also downloads a local search model in the background (about 390 MB, once), and says once when it is set up. Until it is ready, search works by keyword. See [Semantic search](#semantic-search).
|
|
67
|
+
- Prefer GitHub? `pi install git:github.com/galyavin/pi-tuck@v1.0.19`.
|
|
68
|
+
- To pin a version, add it: `pi install npm:pi-tuck@1.0.19`.
|
|
69
69
|
|
|
70
70
|
## Uninstall
|
|
71
71
|
|
|
@@ -102,10 +102,11 @@ pi-tuck's tools appear only after its first cut. Before that there is nothing ou
|
|
|
102
102
|
|
|
103
103
|
| Command | Does |
|
|
104
104
|
|---|---|
|
|
105
|
+
| `/tuck` | Says in words what pi-tuck is doing in this session: the version this session loaded (an open session keeps it until Pi restarts), how many cuts pi-tuck made and the last one's size before and after, whether the cut point adapts or is fixed by `cutAt`, and how search works here. It is for you; the model sees nothing new. |
|
|
105
106
|
| `/tuck-semantic on\|off\|status` | Turns semantic search on or off for this session, or shows its state. |
|
|
106
107
|
| `/tuck-incident on\|next\|off\|status` | Records this session's compaction decisions to a file you can attach to a bug report. |
|
|
107
108
|
|
|
108
|
-
`/compact` works as usual,
|
|
109
|
+
`/compact` works as usual. Text after it does not steer the summary, because pi-tuck builds the summary from the session itself: since 1.0.19 it cuts as plain `/compact` does and says the text was not used. Pi's own setting `"compaction": { "enabled": false }` turns compaction off.
|
|
109
110
|
|
|
110
111
|
## What is proven, and what is not
|
|
111
112
|
|
package/dist/src/build-id.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const BUILD_ID = "
|
|
1
|
+
export declare const BUILD_ID = "27846aedbe111110535eeed5998c572182cd15de0efede0fe4df1571166a838e";
|
package/dist/src/build-id.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const BUILD_ID = "
|
|
1
|
+
export const BUILD_ID = "27846aedbe111110535eeed5998c572182cd15de0efede0fe4df1571166a838e";
|
package/dist/src/compaction.d.ts
CHANGED
|
@@ -39,9 +39,9 @@ export interface CompactionOptions {
|
|
|
39
39
|
* which is the room less 2% of it, or this fraction of the room when that is lower; a lower value cuts earlier and leaves less. The exact room
|
|
40
40
|
* stays the limit of every throw, fault and the complete-context guard. If omitted, the band adapts to the last cut. */
|
|
41
41
|
cutAt?: number;
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
/** A host's response reserve: without `responseExact: true`, the catalogue maximum output stays its floor; with it, `response` is exact.
|
|
43
|
+
* Standalone uses Pi's reserveTokens until a length stop or a context-overflow refusal on the current branch, then also holds the catalogue maximum.
|
|
44
|
+
* Resume, fork and /tree follow that branch's history; a new session has no inherited fallback. */
|
|
45
45
|
requestReserve?: (ctx: ExtensionContext) => {
|
|
46
46
|
response: number;
|
|
47
47
|
responseExact?: true;
|
package/dist/src/compaction.js
CHANGED
|
@@ -290,16 +290,18 @@ export async function installCompaction(pi, options = {}) {
|
|
|
290
290
|
pi.on("agent_settled", () => { admittedRun = false; pending = undefined; phase = "idle"; forgetFailure(); clips.clear(); promptCut = undefined; });
|
|
291
291
|
const active = () => options.active?.() ?? true;
|
|
292
292
|
const settingsFor = () => options.settings ?? SettingsManager.inMemory(pi.getSettings());
|
|
293
|
-
// The
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
//
|
|
297
|
-
// a reply declares an exact reserve (`responseExact: true`) and is held to it; without that flag the catalogue maximum is the floor.
|
|
293
|
+
// The one reserve decision; every consumer takes its room from limits. Standalone starts at Pi's native cut point. Once a reply did
|
|
294
|
+
// not fit (a length stop, or a refusal pi-ai classes as a context overflow), hold the catalogue maximum too. Other errors, such as a 503,
|
|
295
|
+
// keep native's room. Read the whole branch, including history before compactions, each time: no flag to persist or reset on resume,
|
|
296
|
+
// fork, /tree or a new session.
|
|
298
297
|
const requestReserve = (ctx) => {
|
|
299
|
-
const
|
|
298
|
+
const host = options.requestReserve?.(ctx);
|
|
299
|
+
const budget = host ?? { response: settingsFor().getCompactionSettings(ctx.model).reserveTokens };
|
|
300
300
|
if (!Number.isSafeInteger(budget.response) || budget.response < 0)
|
|
301
301
|
throw new Error("Invalid host request reserve");
|
|
302
|
-
|
|
302
|
+
const floor = host ? host.responseExact !== true : ctx.sessionManager.getBranch().some(entry => entry.type === "message" && entry.message.role === "assistant" &&
|
|
303
|
+
(entry.message.stopReason === "length" || (entry.message.stopReason === "error" && isContextOverflow(entry.message))));
|
|
304
|
+
return { response: floor ? Math.max(budget.response, ctx.model?.maxTokens ?? 0) : budget.response };
|
|
303
305
|
};
|
|
304
306
|
const inputRoom = (response, ctx) => {
|
|
305
307
|
const configured = options.activeInputTokens;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RecallPolicy } from './recall.js';
|
|
2
2
|
import type { SessionEntry } from '@earendil-works/pi-coding-agent';
|
|
3
|
-
export declare const DIAGNOSTIC_BUILD = "
|
|
3
|
+
export declare const DIAGNOSTIC_BUILD = "27846aedbe111110535eeed5998c572182cd15de0efede0fe4df1571166a838e";
|
|
4
4
|
export interface Receipt {
|
|
5
5
|
id: string;
|
|
6
6
|
build: string;
|
package/package.json
CHANGED
package/src/build-id.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const BUILD_ID = "
|
|
1
|
+
export const BUILD_ID = "27846aedbe111110535eeed5998c572182cd15de0efede0fe4df1571166a838e";
|
package/src/compaction.ts
CHANGED
|
@@ -109,9 +109,9 @@ export interface CompactionOptions {
|
|
|
109
109
|
* which is the room less 2% of it, or this fraction of the room when that is lower; a lower value cuts earlier and leaves less. The exact room
|
|
110
110
|
* stays the limit of every throw, fault and the complete-context guard. If omitted, the band adapts to the last cut. */
|
|
111
111
|
cutAt?: number;
|
|
112
|
-
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
112
|
+
/** A host's response reserve: without `responseExact: true`, the catalogue maximum output stays its floor; with it, `response` is exact.
|
|
113
|
+
* Standalone uses Pi's reserveTokens until a length stop or a context-overflow refusal on the current branch, then also holds the catalogue maximum.
|
|
114
|
+
* Resume, fork and /tree follow that branch's history; a new session has no inherited fallback. */
|
|
115
115
|
requestReserve?: (ctx: ExtensionContext) => { response: number; responseExact?: true };
|
|
116
116
|
/** The exact messages the host's `context` hook adds to every final request beyond the saved conversation (for example a role
|
|
117
117
|
* view), in the form the hook injects them. Cut triggers, the cut planner and the post-cut checks count them with the same
|
|
@@ -266,17 +266,19 @@ export async function installCompaction(pi: ExtensionAPI, options: CompactionOpt
|
|
|
266
266
|
pi.on("agent_settled", () => { admittedRun = false; pending = undefined; phase = "idle"; forgetFailure(); clips.clear(); promptCut = undefined; });
|
|
267
267
|
const active = () => options.active?.() ?? true;
|
|
268
268
|
const settingsFor = () => options.settings ?? SettingsManager.inMemory(pi.getSettings());
|
|
269
|
-
// The
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// The one place the response reserve is decided; every consumer takes its room from here. A host that knows how its transport caps
|
|
274
|
-
// a reply declares an exact reserve (`responseExact: true`) and is held to it; without that flag the catalogue maximum is the floor.
|
|
269
|
+
// The one reserve decision; every consumer takes its room from limits. Standalone starts at Pi's native cut point. Once a reply did
|
|
270
|
+
// not fit (a length stop, or a refusal pi-ai classes as a context overflow), hold the catalogue maximum too. Other errors, such as a 503,
|
|
271
|
+
// keep native's room. Read the whole branch, including history before compactions, each time: no flag to persist or reset on resume,
|
|
272
|
+
// fork, /tree or a new session.
|
|
275
273
|
const requestReserve = (ctx: ExtensionContext) => {
|
|
276
|
-
const
|
|
274
|
+
const host = options.requestReserve?.(ctx);
|
|
275
|
+
const budget = host ?? { response: settingsFor().getCompactionSettings(ctx.model).reserveTokens };
|
|
277
276
|
if (!Number.isSafeInteger(budget.response) || budget.response < 0)
|
|
278
277
|
throw new Error("Invalid host request reserve");
|
|
279
|
-
|
|
278
|
+
const floor = host ? host.responseExact !== true : ctx.sessionManager.getBranch().some(entry =>
|
|
279
|
+
entry.type === "message" && entry.message.role === "assistant" &&
|
|
280
|
+
(entry.message.stopReason === "length" || (entry.message.stopReason === "error" && isContextOverflow(entry.message))));
|
|
281
|
+
return { response: floor ? Math.max(budget.response, ctx.model?.maxTokens ?? 0) : budget.response };
|
|
280
282
|
};
|
|
281
283
|
const inputRoom = (response: number, ctx: ExtensionContext) => {
|
|
282
284
|
const configured = options.activeInputTokens;
|