pi-fold 4.2.2 → 4.2.3

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/README.md CHANGED
@@ -44,7 +44,7 @@ The forty-column bar uses full-height half-blocks for eighty steps of fill, read
44
44
 
45
45
  The suffix reads `34% · commit at 55% · N Folds (X Cons., Y Span, Z Tool, X Mark, Y Pin)`, naming only the kinds with something to count, in the bar's order; a fresh session reads `0 Folds`. `N Folds` counts visible compressed folds; `Mark` counts pending marks and `Pin` counts protected entries, not additional compressed folds. A tool fold is lossless compression of tool-result context, not a count of tool calls. `/fold-status` retains the detailed token and nesting figures. Its `Budget` row and the editor's `Budget` header report the separate serving budget and the tokens remaining before a commit.
46
46
 
47
- Two dotted marks stand in the band's edge cells: one where a commit aims to land and one where automatic folding is triggered. `minTarget` and `maxTarget` remain shares of the serving budget; the marks and displayed commit point are converted to Pi's full-window scale, so the trigger does not move. With a 272,000-token window and the default 255,616-token serving budget, the 80% trigger appears at about 75% of Pi's window. Percentages are rounded to whole numbers; a custom footer may round differently. Each is drawn over the fill's own colour in a deeper tone of that same hue, so the fill continues behind the mark; on the empty track they stand in muted ink. Neither takes a colour of its own or a warning colour. When Pi reports unknown usage, including immediately after native compaction, the row says `not measured yet`; no saved count or local estimate fills the gap. After a fold commit it identifies the old reading until the provider measures again. Crossing the target is routine runtime work, not a user action item. On reload, saved session context supplies the fold counts and composition without a new model message; the next real context event replaces that read-only view. Unavailable composition is stated as `mapping`; if folding stops, `FOLDING STOPPED` replaces the bar. Non-truecolor terminals use the theme's own colours.
47
+ Two dotted marks stand in the band's edge cells: one where a commit aims to land and one where automatic folding is triggered. `minTarget` and `maxTarget` remain shares of the serving budget; the marks and displayed commit point are converted to Pi's full-window scale, so the trigger does not move. With a 272,000-token window and the default 255,616-token serving budget, the 80% trigger appears at about 75% of Pi's window. The usage label rounds down to a whole percentage, matching pi-cc's footer; bar fill and folding decisions keep the unrounded value. The displayed commit point rounds to the nearest whole percentage. Each is drawn over the fill's own colour in a deeper tone of that same hue, so the fill continues behind the mark; on the empty track they stand in muted ink. Neither takes a colour of its own or a warning colour. When Pi reports unknown usage, including immediately after native compaction, the row says `not measured yet`; no saved count or local estimate fills the gap. After a fold commit it identifies the old reading until the provider measures again. Crossing the target is routine runtime work, not a user action item. On reload, saved session context supplies the fold counts and composition without a new model message; the next real context event replaces that read-only view. Unavailable composition is stated as `mapping`; if folding stops, `FOLDING STOPPED` replaces the bar. Non-truecolor terminals use the theme's own colours.
48
48
 
49
49
  ## Results
50
50
 
@@ -757,7 +757,7 @@ export function registerActiveContext(pi: any, options: {
757
757
  // and post-compaction unknown state. Never substitute our saved provider measurement
758
758
  // or projection estimate. The band remains a share of the SERVING budget: convert its
759
759
  // points onto Pi's full-window axis, without changing any scheduling arithmetic.
760
- const foldBarUsage = (ctx: any): Pick<FoldBarModel, "share" | "commitShare" | "aimShare"> => {
760
+ const foldBarUsage = (ctx: any): Pick<FoldBarModel, "share" | "percent" | "commitShare" | "aimShare"> => {
761
761
  let usage: unknown;
762
762
  try { usage = ctx.getContextUsage?.(); } catch { }
763
763
  const reportedWindow = ownValue(usage, "contextWindow");
@@ -778,7 +778,8 @@ export function registerActiveContext(pi: any, options: {
778
778
  usedTokens: null,
779
779
  });
780
780
  const budgetShare = capacity.budgetTokens / window;
781
- return { share, commitShare: thresholds.maxTarget * budgetShare,
781
+ return { share, percent: share === null ? null : percent as number,
782
+ commitShare: thresholds.maxTarget * budgetShare,
782
783
  aimShare: thresholds.minTarget * budgetShare };
783
784
  };
784
785
  const installFoldBar = (ctx: any): void => {
@@ -802,7 +803,8 @@ export function registerActiveContext(pi: any, options: {
802
803
  }, { placement: "belowEditor" });
803
804
  };
804
805
  const foldBarModel = (ctx: any, input: {
805
- share: number | null; commitShare: number; aimShare: number; staged: number; weighed: boolean;
806
+ share: number | null; percent?: number | null;
807
+ commitShare: number; aimShare: number; staged: number; weighed: boolean;
806
808
  }): FoldBarModel => {
807
809
  const state = persistence.state;
808
810
  // THE SAME SNAPSHOT /fold-status PRICES AGAINST. `markFreedBytes` answers ZERO, silently,
@@ -816,6 +818,7 @@ export function registerActiveContext(pi: any, options: {
816
818
  const model: FoldBarModel = {
817
819
  brand: brandNoun,
818
820
  share: input.share,
821
+ percent: input.percent,
819
822
  commitShare: input.commitShare,
820
823
  aimShare: input.aimShare,
821
824
  mapped: false, mass: emptyFoldBarMass(),
@@ -914,7 +917,7 @@ export function registerActiveContext(pi: any, options: {
914
917
  if (ladder.automaticFailure) parts.push("FOLDING STOPPED");
915
918
  // Unmeasured is stated, never guessed: an estimate rendered as a fact is the habit
916
919
  // that cost a live session, and this line is the most-read surface there is.
917
- parts.push(share === null ? "not measured" : `${Math.round(share * 100)}% full`);
920
+ parts.push(share === null ? "not measured" : `${Math.floor(usage.percent ?? share * 100)}% full`);
918
921
  // THE LIVE BAND, NOT THE LAST SNAPSHOT'S (2026-08-29). This read the threshold off
919
922
  // `lifecycle.latestSnapshot`, which is derived per context event, so a band changed
920
923
  // in /fold-settings went on being announced at its old value until the next event
@@ -254,6 +254,8 @@ export function foldBarShades(palette: FoldBarPalette, dark: boolean): Record<Fo
254
254
  export interface FoldBarModel {
255
255
  brand: string;
256
256
  share: number | null;
257
+ /** Pi's original percent avoids round-trip drift when flooring the usage label. */
258
+ percent?: number | null;
257
259
  commitShare: number;
258
260
  aimShare: number;
259
261
  /** Estimated visible mass, counted exactly once, in the category order above. */
@@ -355,7 +357,7 @@ export function renderFoldBar(model: FoldBarModel, width: number, theme: FoldBar
355
357
  const muted = (text: string): string => theme.fg("muted", text);
356
358
  const brand = theme.fg("dim", model.brand);
357
359
  if (model.stopped) return cut(`${brand} ${theme.fg("error", theme.bold("FOLDING STOPPED"))}` +
358
- (model.share === null ? "" : neutral(` · ${Math.round(model.share * 100)}% full`)));
360
+ (model.share === null ? "" : neutral(` · ${Math.floor(model.percent ?? model.share * 100)}% full`)));
359
361
  if (model.share === null) return cut(`${brand} ${muted(`not measured yet · folds automatically at ${Math.round(model.commitShare * 100)}%`)}`);
360
362
  const truecolorMode = theme.getColorMode?.() === "truecolor";
361
363
  const dark = !lightBackground(theme);
@@ -429,7 +431,7 @@ export function renderFoldBar(model: FoldBarModel, width: number, theme: FoldBar
429
431
  bar += bg ? `${bg}${ink(left as FoldBarKind, "▌")}\x1b[49m` : solid(left);
430
432
  }
431
433
  }
432
- const pct = Math.round(model.share * 100);
434
+ const pct = Math.floor(model.percent ?? model.share * 100);
433
435
  const parts: string[] = [];
434
436
  if (model.staleAfterCommit) parts.push(muted(`${pct}% before the commit`));
435
437
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fold",
3
- "version": "4.2.2",
3
+ "version": "4.2.3",
4
4
  "description": "Lossless context folding for the Pi coding agent: only the oldest context changes; the fresh window stays untouched.",
5
5
  "keywords": [
6
6
  "pi-package",