pi-tuck 1.0.27 → 1.0.28
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 +27 -0
- package/README.md +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +37 -24
- package/dist/src/build-id.d.ts +1 -1
- package/dist/src/build-id.js +1 -1
- package/dist/src/compaction.d.ts +4 -0
- package/dist/src/compaction.js +156 -176
- package/dist/src/diagnostics.d.ts +1 -1
- package/dist/src/request-capacity.d.ts +6 -1
- package/dist/src/request-capacity.js +15 -10
- package/dist/src/run.d.ts +52 -0
- package/dist/src/run.js +89 -0
- package/dist/src/semantic-recall.d.ts +72 -2
- package/dist/src/semantic-recall.js +58 -12
- package/dist/src/status.d.ts +1 -0
- package/dist/src/status.js +2 -1
- package/dist/src/tuck.js +20 -20
- package/index.ts +31 -22
- package/package.json +1 -1
- package/src/build-id.ts +1 -1
- package/src/compaction.ts +137 -159
- package/src/request-capacity.ts +14 -8
- package/src/run.ts +91 -0
- package/src/semantic-recall.ts +46 -14
- package/src/status.ts +3 -1
- package/src/tuck.ts +18 -18
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
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.28] - 2026-11-10
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- **One owner for each piece of state that had several, the user-approved state refactor.** Requests are unchanged.
|
|
11
|
+
- The **pending cut** has one view, which every consumer reads: the request, `tuck_search`, the history tools, Pi's threshold path, the
|
|
12
|
+
band, the boundary save, `/tuck`, and the clamp, deepest-cut and retry previews.
|
|
13
|
+
- The **run** is one record, reset in one place. It holds the starting prompt as an entry, a typed prompt or a custom message.
|
|
14
|
+
- **Reply fit** has one reader behind the reserve and the floor.
|
|
15
|
+
- **Semantic setup** has an explicit state, which `/tuck` and the notices derive from.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **A plan made for a prompt that another extension handled could still answer Pi's own compaction check before a later run.** A plan now
|
|
20
|
+
answers that check only until the next tick, unless its own run starts. The cut it made was valid but deeper than needed.
|
|
21
|
+
|
|
22
|
+
### Release gates
|
|
23
|
+
|
|
24
|
+
- **Shipped without a live gate, because its replay matches 1.0.27:** 13,946 requests on 29 recordings, 0 lost and 0 dead tasks, clips
|
|
25
|
+
17 → 17. In the final run, one recording (live32-t02-M) showed 32 differing requests. Two more runs of this build on that recording matched
|
|
26
|
+
1.0.27 exactly, as did two more runs of 1.0.27, so it was noise.
|
|
27
|
+
- **Strict stress:** 0 findings.
|
|
28
|
+
- **The lifecycle sequence test:** 41 seeds and 1,107 events, clean.
|
|
29
|
+
- **An independent review** found 2 notice-only differences from 1.0.27, both fixed with tests: the length-stop notice after a tree move
|
|
30
|
+
inside another extension's settle, and the "set up" notice for a setup that finishes in a later session.
|
|
31
|
+
- Tests (802) and CI. The packed tarball loaded in the real installed Pi.
|
|
32
|
+
|
|
6
33
|
## [1.0.27] - 2026-11-10
|
|
7
34
|
|
|
8
35
|
### Fixed
|
package/README.md
CHANGED
|
@@ -64,8 +64,8 @@ Then start a new Pi session. That is all. pi-tuck takes over Pi's compaction, bo
|
|
|
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
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.
|
|
68
|
-
- To pin a version, add it: `pi install npm:pi-tuck@1.0.
|
|
67
|
+
- Prefer GitHub? `pi install git:github.com/galyavin/pi-tuck@v1.0.27`.
|
|
68
|
+
- To pin a version, add it: `pi install npm:pi-tuck@1.0.27`.
|
|
69
69
|
|
|
70
70
|
## Uninstall
|
|
71
71
|
|
|
@@ -135,6 +135,7 @@ Known limits today:
|
|
|
135
135
|
- A request above the room could be admitted and then refused, leaving the session stuck where native Pi would have sent (1.0.22 to 1.0.24; fixed in 1.0.25). pi-tuck checked its line in its own corrected token count, while Pi's reply cap uses Pi's own estimate, which is higher when the provider counts fewer tokens than pi-tuck's raw measure ([`CHANGELOG.md`](CHANGELOG.md) 1.0.25).
|
|
136
136
|
- Near the 1.0.22 refusal line, replies were cut to a few hundred tokens (1.0.22; fixed in 1.0.23). 1.0.22 left only its reply floor and did not count Pi's 4,096-token margin on the reply cap ([`CHANGELOG.md`](CHANGELOG.md) 1.0.23).
|
|
137
137
|
- Cost of the 1.0.22 change: between the room and the window, the reply reserve no longer cushions a wrong count. At a cold start an undercount can reach the provider. A provider that errors is caught and learned from; a server that truncates silently truncates, as it does for native Pi. With a setup above the room, cuts are forced and frequent, each freeing little, which beats a stranded session ([`CHANGELOG.md`](CHANGELOG.md) 1.0.22).
|
|
138
|
+
- A run that a custom message started could lose that message after a cut in the middle of the run (fixed in 1.0.27). pi-tuck protected only a prompt the user typed, so a tool-loop cut in a run an extension started with a custom message kept the message neither whole nor quoted, and later requests lacked the instruction that started the run. Now such a message gets the same protection as a typed prompt: it is kept, or quoted whole in the summary, and the guard checks it. A custom message with no text has nothing to quote and stays unprotected ([`CHANGELOG.md`](CHANGELOG.md) 1.0.27).
|
|
138
139
|
- A prompt's cut could reach a run it was not planned for (fixed in 1.0.24). pi-tuck plans a prompt's cut when the prompt arrives. When another extension handled that prompt, no run started, and the plan waited for the next run. A run started by a custom message carried a cut sized for the prompt that never ran, deeper than needed. After a move in the session tree (`/tree`), the plan was applied on the new branch, where its kept entry was not present, so the request lost the custom message that started the run, even on a branch that needed no cut ([`CHANGELOG.md`](CHANGELOG.md) 1.0.24).
|
|
139
140
|
- Four `tuck_search` faults, fixed in 1.0.23: it said semantic recall was still indexing when the history was fully indexed; in the turn after a prompt's cut it ranked nothing ("nothing is out of view"); a keyword-search timeout on a very long history failed the whole search, where now the semantic results still answer; and the timeout message named a pattern instead of the history's size ([`CHANGELOG.md`](CHANGELOG.md) 1.0.23).
|
|
140
141
|
- pi-tuck cut earlier than native Pi when a model's reply cap is above Pi's reserve (1.0.0 to 1.0.19; fixed in 1.0.20). It reserved the larger of Pi's `compaction.reserveTokens` 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 compacts at 65,536 ([`CHANGELOG.md`](CHANGELOG.md) 1.0.20).
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { CompactionOptions } from "./src/compaction.js";
|
|
|
3
3
|
export { installCompaction } from "./src/compaction.js";
|
|
4
4
|
export { inspectIncident, readableIncident } from "./src/diagnostic-replay.js";
|
|
5
5
|
export { createSemanticRecall, SemanticRecallController } from "./src/semantic-recall.js";
|
|
6
|
-
export type { SemanticRecallConfig, PriorResult, SemanticState } from "./src/semantic-recall.js";
|
|
6
|
+
export type { SemanticRecallConfig, PriorResult, SemanticState, SemanticSetup } from "./src/semantic-recall.js";
|
|
7
7
|
export { ensureSemanticRuntime } from "./src/setup.js";
|
|
8
8
|
/** pi-tuck as a Pi extension: compaction and history recall always; semantic recall too, unless `pi-tuck.json` in Pi's agent folder
|
|
9
9
|
* says `{"semanticRecall": false}`. Its runtime is set up in the background the first time a session starts. The same file's `"cutAt"`
|
package/dist/index.js
CHANGED
|
@@ -43,14 +43,21 @@ export default async function (pi) {
|
|
|
43
43
|
}
|
|
44
44
|
const semanticRecall = createSemanticRecall();
|
|
45
45
|
// `/tuck-semantic off` holds for the rest of the session, including over a setup that finishes later.
|
|
46
|
-
let off = false;
|
|
47
46
|
const settings = readSettings(join(getAgentDir(), "pi-tuck.json"));
|
|
48
47
|
// Semantic recall runs on Metal, so only on Apple silicon; elsewhere the model is not offered tuck_search at all.
|
|
49
48
|
const appleSilicon = process.platform === "darwin" && process.arch === "arm64";
|
|
50
|
-
const handle = await installCompaction(pi, { semanticRecall, search: appleSilicon, cutAt: settings.cutAt, onSemanticSetting: async (enabled) => {
|
|
51
|
-
|
|
49
|
+
const handle = await installCompaction(pi, { semanticRecall, search: appleSilicon, cutAt: settings.cutAt, onSemanticSetting: async (enabled) => {
|
|
50
|
+
if (!enabled)
|
|
51
|
+
semanticRecall.setSetup("off-user");
|
|
52
|
+
else if (semanticRecall.status().setup === "off-user")
|
|
53
|
+
semanticRecall.setSetup("starting", { installed: existsSync(runtimeConfigPath(defaultRoot())) });
|
|
54
|
+
} });
|
|
55
|
+
if (settings.semanticRecall === false) {
|
|
56
|
+
semanticRecall.setSetup("off-settings");
|
|
57
|
+
pi.on("session_start", () => { semanticRecall.setSetup("off-settings"); });
|
|
52
58
|
return;
|
|
53
|
-
|
|
59
|
+
}
|
|
60
|
+
let session = 0, said = "", announced = false, setup;
|
|
54
61
|
// A context outlives its session (a setup can finish after /new); a notice to an ended session is dropped.
|
|
55
62
|
// With no UI (pi -p), Pi shows no notices, so a settings error or an off or failing semantic recall would go unsaid: write each to stderr
|
|
56
63
|
// instead, once per session. stderr is the operator's; it never enters the model's context.
|
|
@@ -75,49 +82,55 @@ export default async function (pi) {
|
|
|
75
82
|
said = message;
|
|
76
83
|
say(ctx, message, level);
|
|
77
84
|
} };
|
|
85
|
+
const announce = (ctx, state, detail) => {
|
|
86
|
+
semanticRecall.setSetup(state, detail);
|
|
87
|
+
const notice = semanticRecall.setupNotice();
|
|
88
|
+
if (notice)
|
|
89
|
+
(state === "off-platform" || state === "off-project" ? once : say)(ctx, notice.message, notice.level);
|
|
90
|
+
};
|
|
78
91
|
pi.on("session_shutdown", () => { session++; });
|
|
79
92
|
pi.on("session_start", (_event, ctx) => {
|
|
80
93
|
const current = ++session;
|
|
81
|
-
off = false;
|
|
82
94
|
stderrSaid.clear();
|
|
83
95
|
if (settings.error)
|
|
84
96
|
once(ctx, `pi-tuck: ${settings.error}; using the defaults.`, "warning");
|
|
85
97
|
if (!appleSilicon)
|
|
86
|
-
return
|
|
98
|
+
return announce(ctx, "off-platform");
|
|
87
99
|
// Semantic recall keeps its runtime and indexes outside the project it serves, so not when Pi runs in a folder that holds its
|
|
88
100
|
// agent folder, such as the home folder.
|
|
89
101
|
if (within(real(getAgentDir()), real(ctx.cwd)))
|
|
90
|
-
return
|
|
102
|
+
return announce(ctx, "off-project");
|
|
91
103
|
// A run with no UI (pi -p, RPC, JSON) uses a runtime that is already installed: that needs no download. It does not start a first-time
|
|
92
104
|
// setup, which would outlast a one-shot run, and it says why, so tuck_search and /tuck name the real state.
|
|
93
105
|
const installed = existsSync(runtimeConfigPath(defaultRoot()));
|
|
94
|
-
if (!ctx.hasUI && !installed)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
if (!ctx.hasUI && !installed)
|
|
107
|
+
return announce(ctx, "not-installed");
|
|
108
|
+
// The download's announcement outlives the session that made it: a session that starts while the shared setup is still running
|
|
109
|
+
// owes the "set up" notice once, as before the refactor.
|
|
110
|
+
semanticRecall.setSetup("starting", { installed, announced });
|
|
111
|
+
setup ??= ensureSemanticRuntime(defaultRoot(), message => {
|
|
112
|
+
announced = true;
|
|
113
|
+
if (current === session && semanticRecall.status().setup !== "off-user")
|
|
114
|
+
announce(ctx, "starting", { installed, notice: message, announced: true });
|
|
115
|
+
});
|
|
101
116
|
setup.then(async () => {
|
|
102
|
-
if (current !== session || off)
|
|
117
|
+
if (current !== session || semanticRecall.status().setup === "off-user")
|
|
103
118
|
return;
|
|
104
119
|
try {
|
|
105
|
-
await handle.setSemanticEnabled(true, ctx);
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
await handle.setSemanticEnabled(true, ctx);
|
|
121
|
+
if (current === session && semanticRecall.status().setup !== "off-user") {
|
|
122
|
+
announce(ctx, "ready", { announced });
|
|
108
123
|
announced = false;
|
|
109
|
-
say(ctx, "pi-tuck: semantic recall is set up. Search by meaning works from the first cut on.", "info");
|
|
110
124
|
}
|
|
111
125
|
}
|
|
112
126
|
catch (error) {
|
|
113
|
-
if (current === session)
|
|
114
|
-
|
|
127
|
+
if (current === session && semanticRecall.status().setup !== "off-user")
|
|
128
|
+
announce(ctx, "failed", { stage: "start", reason: reason(error) });
|
|
115
129
|
}
|
|
116
130
|
}, error => {
|
|
117
131
|
setup = undefined; // the next session start tries again
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
say(ctx, `pi-tuck: semantic recall setup failed (${reason(error)}). Word recall works; setup retries when a session starts.`, "warning");
|
|
132
|
+
if (current === session && semanticRecall.status().setup !== "off-user")
|
|
133
|
+
announce(ctx, "failed", { stage: "setup", reason: reason(error) });
|
|
121
134
|
});
|
|
122
135
|
});
|
|
123
136
|
}
|
package/dist/src/build-id.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const BUILD_ID = "
|
|
1
|
+
export declare const BUILD_ID = "e9c750b757776bf44ad79939b633f178ef9a8f14acfab1c8bb6311d692c79e61";
|
package/dist/src/build-id.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const BUILD_ID = "
|
|
1
|
+
export const BUILD_ID = "e9c750b757776bf44ad79939b633f178ef9a8f14acfab1c8bb6311d692c79e61";
|
package/dist/src/compaction.d.ts
CHANGED
|
@@ -74,6 +74,8 @@ export declare function installCompaction(pi: ExtensionAPI, options?: Compaction
|
|
|
74
74
|
close: () => Promise<void>;
|
|
75
75
|
semanticRecall: SemanticRecallController;
|
|
76
76
|
setSemanticEnabled: (enabled: boolean, ctx: ExtensionContext) => Promise<{
|
|
77
|
+
setup: import("./semantic-recall.js").SemanticSetup;
|
|
78
|
+
setupMessage: string | null;
|
|
77
79
|
enabled: boolean;
|
|
78
80
|
configured: boolean;
|
|
79
81
|
cleanupError: string | null;
|
|
@@ -119,6 +121,8 @@ export declare function installCompaction(pi: ExtensionAPI, options?: Compaction
|
|
|
119
121
|
semanticStatus: () => {
|
|
120
122
|
host?: unknown;
|
|
121
123
|
local: {
|
|
124
|
+
setup: import("./semantic-recall.js").SemanticSetup;
|
|
125
|
+
setupMessage: string | null;
|
|
122
126
|
enabled: boolean;
|
|
123
127
|
configured: boolean;
|
|
124
128
|
cleanupError: string | null;
|