@openlines/opl-pi-sht 0.2.5 → 0.2.6

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
@@ -17,7 +17,7 @@ A portable collection of various Pi coding agent extensions. Repository director
17
17
  <div align="center">
18
18
 
19
19
  >
20
- > ### v0.2.5 (24 Sept 2026) - **Dev dependency floor**: `@earendil-works/pi-coding-agent` is now `^0.87.0` <br/>see [CHANGELOG.md](CHANGELOG.md) for more details.
20
+ > ### v0.2.6 (24 Sept 2026) - **Post-compaction context estimate**: `opl-footer` keeps the context bar visible with explicitly approximate values until Pi reports fresh usage. <br/>see [CHANGELOG.md](CHANGELOG.md) for more details.
21
21
  >
22
22
 
23
23
  </div>
@@ -75,7 +75,7 @@ See the tracked [`configs/opl-footer.json.sample`](../../configs/opl-footer.json
75
75
  | `model` | Model name in pink + `(provider)` in dim | No icon; provider omitted if unavailable |
76
76
  | `path` | Current working directory | `segmentOptions.path.mode`: `"full"` (default) · `"abbreviated"` · `"basename"` |
77
77
  | `git` | Git branch and dirty indicators | `showBranch`, `showStaged`, `showUnstaged`, `showUntracked` (all bool) |
78
- | `context_pct` | Gradient bar + `X.X%` + max tokens | Bar fully configurable via `segmentOptions.contextBar` (see below). % and max tokens use `contextLabel` colour. Max tokens formatted with K/M suffix (e.g. `128k`, `2M`). Usage comes from `ctx.getContextUsage()`; when Pi reports it as unknown (right after compaction or a context edit) the segment renders an explicit `(--%)` instead of a stale value. Set `DEBUG_PCT` in `context.ts` to a number (0–100) to pin the bar at a fixed value for visual testing. |
78
+ | `context_pct` | Gradient bar + `X.X%` + max tokens | Bar fully configurable via `segmentOptions.contextBar` (see below). % and max tokens use `contextLabel` colour. Max tokens formatted with K/M suffix (e.g. `128k`, `2M`). Usage comes from `ctx.getContextUsage()`; when Pi reports it as unknown right after compaction, the segment estimates the rebuilt projection with Pi’s `estimateTokens()` and marks the bar and values with `≈` until a fresh assistant response provides exact usage. If no projection is available, it renders `(--%)` rather than a stale value. Set `DEBUG_PCT` in `context.ts` to a number (0–100) to pin the bar at a fixed value for visual testing. |
79
79
  | `cost` | `$<amount>` (4 decimals, e.g. `$0.0123`) | `$` dim, amount in `cost` colour (`muted` by default). Four decimals keep cheap local or short sessions distinguishable instead of pinning at `$0.00`. Shows dim `(no pricing)` when the session total is zero on a non-local model (provider has no pricing configured), and `(local model)` for local models |
80
80
  | `thinking` | `Thinking: <LEVEL>` | Dim label, CAPS level with per-level colour; always visible |
81
81
  | `mode_switcher` | Unified active mode label | Reads state published by `opl-modes`; `appearance.modeColor` controls the mode value, with hardcoded `muted` fallback |
@@ -5,6 +5,7 @@ import type { TUI } from "@earendil-works/pi-tui";
5
5
 
6
6
  import type { SegmentContext, StatusLineSegmentId, UsageStats, SessionStats, SessionEvent, ThinkingLevelEvent, AssistantMessageEvent, ToolResultEvent, UserBashEvent } from "./types.js";
7
7
  import { renderSegment } from "./segments/index.js";
8
+ import { estimateContextUsage } from "./segments/context.js";
8
9
  import { createAgentStatusTracker } from "./segments/status.js";
9
10
  import { getGitStatus, invalidateGitStatus, invalidateGitBranch } from "./git-status.js";
10
11
  import { getEffectiveConfig } from "./config.js";
@@ -278,12 +279,16 @@ export default function footer(pi: ExtensionAPI) {
278
279
  ? thinkingEvents.reduce((_, e) => e.thinkingLevel ?? "off", "off")
279
280
  : null;
280
281
 
281
- // Prefer Pi's canonical context usage (0.87), which is null when usage is unknown
282
- // (e.g. right after compaction, before the next assistant reply) or after a context
283
- // edit. Fall back to the model's window when unavailable.
282
+ // Prefer Pi's canonical context usage (0.87). Immediately after compaction it is
283
+ // null until the next assistant reply, so estimate the rebuilt projection instead.
284
284
  const usage = typeof ctx.getContextUsage === "function" ? ctx.getContextUsage() : undefined;
285
285
  const contextWindow = usage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
286
- const contextPercent = usage?.percent ?? null;
286
+ const projectedMessages = ctx.sessionManager?.buildSessionProjection?.().messages;
287
+ const estimatedContextUsage = usage?.percent === null && contextWindow > 0 && projectedMessages
288
+ ? estimateContextUsage(projectedMessages, contextWindow)
289
+ : null;
290
+ const contextPercent = usage?.percent ?? estimatedContextUsage?.percent ?? null;
291
+ const contextEstimated = estimatedContextUsage !== null;
287
292
 
288
293
  // Get git status (cached). Skip the probes entirely when no visible row renders the
289
294
  // git segment — otherwise an unused cell keeps spawning git once per second.
@@ -322,6 +327,7 @@ export default function footer(pi: ExtensionAPI) {
322
327
  sessionId: ctx.sessionManager?.getSessionId?.(),
323
328
  usageStats,
324
329
  contextPercent,
330
+ contextEstimated,
325
331
  contextWindow,
326
332
  usingSubscription,
327
333
  sessionStartTime,
@@ -1,3 +1,5 @@
1
+ import { estimateTokens } from "@earendil-works/pi-coding-agent";
2
+
1
3
  import type { RenderedSegment, SegmentContext } from "../types.js";
2
4
  import { applyColor, resolveColorToRgb } from "../theme.js";
3
5
  import { color } from "./helpers.js";
@@ -19,6 +21,14 @@ export function lerp(a: number, b: number, t: number): number {
19
21
  return Math.round(a + (b - a) * t);
20
22
  }
21
23
 
24
+ export function estimateContextUsage(
25
+ messages: Parameters<typeof estimateTokens>[0][],
26
+ contextWindow: number,
27
+ ): { tokens: number; percent: number } {
28
+ const tokens = messages.reduce((total, message) => total + estimateTokens(message), 0);
29
+ return { tokens, percent: (tokens / contextWindow) * 100 };
30
+ }
31
+
22
32
  function positionColor(
23
33
  pos: number,
24
34
  barWidth: number,
@@ -75,7 +85,9 @@ export const contextPctSegment = {
75
85
 
76
86
  // Show: (00.00%) 99999 / 99999
77
87
  const used = Math.round((pct / 100) * ctx.contextWindow);
78
- const pctLabel = `(${pct.toFixed(2)}%) ${formatTokens(used)} / ${formatTokens(ctx.contextWindow)}`;
88
+ const pctLabel = ctx.contextEstimated
89
+ ? `≈${pct.toFixed(2)}% ≈${formatTokens(used)} / ${formatTokens(ctx.contextWindow)}`
90
+ : `(${pct.toFixed(2)}%) ${formatTokens(used)} / ${formatTokens(ctx.contextWindow)}`;
79
91
  const pctStr = color(ctx, "contextLabel", pctLabel);
80
92
 
81
93
  return { content: `${bar} ${pctStr}`, visible: true };
@@ -119,6 +119,7 @@ export interface SegmentContext {
119
119
  sessionId: string | undefined;
120
120
  usageStats: UsageStats;
121
121
  contextPercent: number | null;
122
+ contextEstimated: boolean;
122
123
  contextWindow: number;
123
124
  usingSubscription: boolean;
124
125
  sessionStartTime: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlines/opl-pi-sht",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "opl-pi-sht (shouldHaveThat) Pi coding-agent extension kit: token-efficient context, safe modes, fingerprinted /init, model benchmarking, web access, and browser automation.",
5
5
  "repository": {
6
6
  "type": "git",