@danypops/jittor 0.1.0 → 0.1.2

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
@@ -35,11 +35,11 @@ Operations currently include `metrics.record`, `metrics.query`, `metrics.prune`,
35
35
 
36
36
  Provider adapters currently include official OpenRouter key/usage/model telemetry and an explicitly experimental Codex subscription adapter. The Codex adapter follows the pinned open-source CLI `/wham/usage` payload and `x-codex-*` response-header contracts, accepts additional metered limits, and fails closed on malformed windows or impossible percentages. File credentials must be explicitly configured and private (`0600`); Jittor reads only the access token and account ID, never refreshes credentials, and never logs or persists OAuth secrets.
37
37
 
38
- The native Pi extension preflights input and every provider turn, applies model/thinking decisions, records response headers and finalized usage through the daemon, and blocks requests when required telemetry is unsafe. It follows Pi's current authenticated model/provider and synchronizes Pi's available models before every decision, so unavailable catalog routes are never selected. Its responsive Alef-style footer groups repository and AI identity with cumulative usage, a color-coded context-window bar, and current-provider budget telemetry. Codex shows a bounded quota bar for its longest percentage window; OpenRouter remains honest about its unbounded raw spend and does not fabricate a denominator. Unknown and stale telemetry are marked explicitly. Run `/jittor` for detailed burn pressure, freshness, route state, and confirmed emergency-halt/override controls.
38
+ The native Pi extension preflights input and every provider turn, applies model/thinking decisions, records response headers and finalized usage through the daemon, and blocks requests when required telemetry is unsafe. It follows Pi's current authenticated model/provider and synchronizes Pi's available models before every decision, so unavailable catalog routes are never selected. Its responsive integrated footer groups repository and model identity with cumulative usage, a color-coded context-window bar, and current-provider budget telemetry. Codex shows the active model's bounded quota as a draining remaining-budget bar with reset and freshness information. OpenRouter uses the same drain semantics when its official key telemetry exposes a configured limit and remaining balance; keys without a limit remain honest text-only spend and never receive a fabricated denominator. During Pi compaction, the context bar drains as an animation and reports elapsed time. Unknown and stale telemetry are marked explicitly. Run `/jittor` for detailed burn pressure, freshness, route state, and confirmed emergency-halt/override controls.
39
39
 
40
- Blocking always has a daemon-independent escape hatch. `/jittor off` (or `/jittor-off`) immediately enters persisted monitor-only mode and never blocks provider requests. The informational footer is independently controlled with `/jittor footer on` and `/jittor footer off`, so showing status never enables enforcement. `/jittor on` only enables enforcement after telemetry polling and available-route synchronization succeed. Every fail-closed error includes these recovery commands plus the daemon restart command.
40
+ Blocking always has a daemon-independent escape hatch. `/jittor off` immediately enters persisted monitor-only mode and never blocks provider requests. The informational footer is independently controlled with `/jittor footer on` and `/jittor footer off`, so showing status never enables enforcement. `/jittor on` only enables enforcement after telemetry polling and available-route synchronization succeed. Every fail-closed error includes these recovery commands plus the daemon restart command.
41
41
 
42
- Run `/usage` for a colored Unicode token histogram with X/Y axes, provider/model series, input/output/cache totals, refresh, and `24h`, `7d`, `30d`, or `90d` ranges. Left/Right changes range and `r` refreshes. Usage is persisted by the daemon from finalized Pi assistant messages.
42
+ Run `/jittor usage` for a colored Unicode token histogram with X/Y axes, provider/model series, input/output/cache totals, refresh, and `24h`, `7d`, `30d`, or `90d` ranges. Left/Right changes range and `r` refreshes. Usage is persisted by the daemon from finalized Pi assistant messages.
43
43
 
44
44
  See [`docs/CALIBRATION.md`](docs/CALIBRATION.md) for thresholds and rollback, and [`docs/USAGE_PRIOR_ART.md`](docs/USAGE_PRIOR_ART.md) for the chart design research.
45
45
 
@@ -1,6 +1,6 @@
1
1
  # Token-usage TUI prior art
2
2
 
3
- Research performed before implementing Jittor's `/usage` frontend.
3
+ Research performed before implementing Jittor's `/jittor usage` frontend.
4
4
 
5
5
  ## Local agent implementations
6
6
 
@@ -60,5 +60,5 @@ Jittor combines the best applicable patterns:
60
60
  3. Provider/model-preserving series and input/output/cache totals.
61
61
  4. Vertically scaled, colored, stacked Unicode bars with fractional top blocks.
62
62
  5. Width-safe X/Y axes and provider/model legend.
63
- 6. Native `/usage` panel with Left/Right range switching and refresh.
63
+ 6. Native `/jittor usage` panel with Left/Right range switching and refresh.
64
64
  7. Data access only through authenticated daemon `metrics.query`; the extension never opens SQLite or reads provider credentials.
@@ -6,8 +6,13 @@ import {
6
6
  FOOTER_BAR_MIN_WIDTH,
7
7
  FOOTER_CONTEXT_ACCENT_FRACTION,
8
8
  FOOTER_CONTEXT_ERROR_FRACTION,
9
+ FOOTER_COMPACTION_DRAIN_STEP_MS,
9
10
  FOOTER_CONTEXT_WARNING_FRACTION,
10
11
  FOOTER_WIDE_TERMINAL_WIDTH,
12
+ MILLISECONDS_PER_DAY,
13
+ MILLISECONDS_PER_HOUR,
14
+ MILLISECONDS_PER_MINUTE,
15
+ MILLISECONDS_PER_SECOND,
11
16
  TELEMETRY_STALE_AFTER_MS,
12
17
  } from "../../src/constants.ts";
13
18
 
@@ -42,12 +47,24 @@ interface FooterContext {
42
47
  };
43
48
  }
44
49
 
45
- /** A provider usage value suitable for the footer. Null fraction means no known denominator. */
46
- export interface ProviderBudget {
50
+ /** A bounded quota is explicitly remaining; unbounded values never receive a fabricated bar. */
51
+ export type ProviderBudget = {
52
+ kind: "bounded";
53
+ label: string;
54
+ remainingFraction: number;
55
+ observedAt?: number;
56
+ resetsAt?: number;
57
+ resetText?: string;
58
+ } | {
59
+ kind: "unbounded";
47
60
  label: string;
48
- fraction: number | null;
49
61
  valueText: string;
50
62
  observedAt?: number;
63
+ };
64
+
65
+ export interface CompactionProgress {
66
+ startedAt: number;
67
+ initialFraction: number;
51
68
  }
52
69
 
53
70
  interface UsageTotals {
@@ -116,24 +133,72 @@ function fillColor(fraction: number | null): FooterColor {
116
133
  return "dim";
117
134
  }
118
135
 
119
- function contextSegment(context: FooterContext, theme: FooterTheme, width: number, compact: boolean): string {
136
+ function compactionFraction(progress: CompactionProgress, width: number, now: number): number {
137
+ const initialFilled = Math.round(Math.min(1, Math.max(0, progress.initialFraction)) * width);
138
+ const drained = Math.floor(Math.max(0, now - progress.startedAt) / FOOTER_COMPACTION_DRAIN_STEP_MS);
139
+ return Math.max(0, initialFilled - drained) / width;
140
+ }
141
+
142
+ function contextSegment(
143
+ context: FooterContext,
144
+ theme: FooterTheme,
145
+ width: number,
146
+ compact: boolean,
147
+ now: number,
148
+ compaction?: CompactionProgress,
149
+ ): string {
150
+ const w = barWidth(width);
151
+ if (compaction) {
152
+ const fraction = compactionFraction(compaction, w, now);
153
+ const elapsedSeconds = Math.floor(Math.max(0, now - compaction.startedAt) / MILLISECONDS_PER_SECOND);
154
+ return `ctx ${theme.fg("accent", progressBar(fraction, w))} compact ${elapsedSeconds}s`;
155
+ }
120
156
  const usage = context.getContextUsage();
121
157
  const window = usage?.contextWindow ?? context.model?.contextWindow ?? 0;
122
158
  const fraction = usage?.percent === null || usage?.percent === undefined ? null : usage.percent / 100;
123
- const bar = theme.fg(fillColor(fraction), progressBar(fraction, barWidth(width)));
159
+ const bar = theme.fg(fillColor(fraction), progressBar(fraction, w));
124
160
  if (usage?.tokens === null || usage?.tokens === undefined) return `ctx ${bar} ?/${formatTokens(window)}`;
125
161
  const value = compact ? `${Math.round((fraction ?? 0) * 100)}%` : `${formatTokens(usage.tokens)}/${formatTokens(window)}`;
126
162
  return `ctx ${bar} ${value}`;
127
163
  }
128
164
 
165
+ function minimalContextSegment(
166
+ context: FooterContext,
167
+ theme: FooterTheme,
168
+ width: number,
169
+ now: number,
170
+ compaction?: CompactionProgress,
171
+ ): string {
172
+ const w = barWidth(width);
173
+ if (compaction) {
174
+ const fraction = compactionFraction(compaction, w, now);
175
+ return `ctx ${theme.fg("accent", progressBar(fraction, w))}`;
176
+ }
177
+ const percent = context.getContextUsage()?.percent;
178
+ const fraction = percent === null || percent === undefined ? null : percent / 100;
179
+ return `ctx ${theme.fg(fillColor(fraction), progressBar(fraction, w))}`;
180
+ }
181
+
182
+ function resetLabel(resetsAt: number | undefined, now: number): string | undefined {
183
+ if (resetsAt === undefined) return undefined;
184
+ const remaining = resetsAt - now;
185
+ if (remaining <= 0) return "reset due";
186
+ if (remaining >= MILLISECONDS_PER_DAY) return `resets in ${Math.floor(remaining / MILLISECONDS_PER_DAY)}d`;
187
+ if (remaining >= MILLISECONDS_PER_HOUR) return `resets in ${Math.floor(remaining / MILLISECONDS_PER_HOUR)}h`;
188
+ return `resets in ${Math.max(1, Math.ceil(remaining / MILLISECONDS_PER_MINUTE))}m`;
189
+ }
190
+
129
191
  function budgetSegment(budget: ProviderBudget | null, theme: FooterTheme, width: number, compact: boolean, now: number): string {
130
192
  const w = barWidth(width);
131
193
  if (!budget) return `budget ${theme.fg("dim", progressBar(null, w))} ?`;
132
- if (budget.fraction === null) return `${budget.label} ${budget.valueText}`;
133
- const bar = theme.fg(fillColor(budget.fraction), progressBar(budget.fraction, w));
134
- const value = compact ? `${Math.round(budget.fraction * 100)}%` : budget.valueText;
135
194
  const stale = budget.observedAt !== undefined && now - budget.observedAt > TELEMETRY_STALE_AFTER_MS;
136
- return `${budget.label} ${bar} ${value}${stale ? ` ${theme.fg("warning", "stale")}` : ""}`;
195
+ const staleText = stale ? ` ${theme.fg("warning", "stale")}` : "";
196
+ if (budget.kind === "unbounded") return `${budget.label} ${budget.valueText}${staleText}`;
197
+ const remaining = Math.min(1, Math.max(0, budget.remainingFraction));
198
+ const bar = theme.fg(fillColor(1 - remaining), progressBar(remaining, w));
199
+ const value = `${(compact ? Math.round(remaining * 100) : (remaining * 100).toFixed(1))}% left`;
200
+ const reset = compact ? undefined : resetLabel(budget.resetsAt, now) ?? budget.resetText;
201
+ return `${budget.label} ${bar} ${value}${reset ? ` · ${reset}` : ""}${staleText}`;
137
202
  }
138
203
 
139
204
  function usageSegment(context: FooterContext): string {
@@ -150,39 +215,33 @@ function usageSegment(context: FooterContext): string {
150
215
  return parts.join(" ");
151
216
  }
152
217
 
153
- function identityLines(context: FooterContext, footerData: FooterData, theme: FooterTheme, thinkingLevel: string, width: number): string[] {
218
+ function repositorySegment(context: FooterContext, footerData: FooterData, theme: FooterTheme): string {
154
219
  let cwd = footerCwd(context.sessionManager.getCwd(), process.env.HOME ?? process.env.USERPROFILE);
155
220
  const branch = footerData.getGitBranch();
156
221
  if (branch) cwd += ` (${branch})`;
157
222
  const sessionName = context.sessionManager.getSessionName();
158
223
  if (sessionName) cwd += ` · ${sessionName}`;
159
- const repo = `${theme.bold("Repo")} ${theme.fg("dim", cwd)}`;
224
+ return theme.fg("dim", cwd);
225
+ }
160
226
 
227
+ function modelSegments(context: FooterContext, footerData: FooterData, theme: FooterTheme, thinkingLevel: string): { full: string; compact: string } {
161
228
  const model = context.model;
229
+ const modelName = theme.bold(model?.id ?? "no-model");
162
230
  const provider = model && footerData.getAvailableProviderCount() > 1 ? `(${model.provider}) ` : "";
163
231
  const thinking = model?.reasoning ? ` · ${thinkingLevel === "off" ? "thinking off" : thinkingLevel}` : "";
164
- const ai = `${theme.bold("AI")} ${provider}${model?.id ?? "no-model"}${thinking}`;
165
- const gap = width - visibleWidth(repo) - visibleWidth(ai);
166
- if (gap >= 2) return [`${repo}${" ".repeat(gap)}${ai}`];
167
- return [truncateToWidth(repo, width, "…"), truncateToWidth(ai, width, "…")];
232
+ return { full: `${provider}${modelName}${thinking}`, compact: modelName };
168
233
  }
169
234
 
170
- function statusLines(context: FooterContext, budget: ProviderBudget | null, theme: FooterTheme, width: number, now: number): string[] {
171
- const usage = usageSegment(context);
172
- const fullContext = contextSegment(context, theme, width, false);
173
- const fullBudget = budgetSegment(budget, theme, width, false, now);
174
- const fullParts = [usage, fullContext, fullBudget].filter(Boolean);
175
- const full = `${theme.bold("LLM")} ${fullParts.join(" · ")}`;
176
- if (visibleWidth(full) <= width) return [full];
177
-
178
- const compactContext = contextSegment(context, theme, width, true);
179
- const compactBudget = budgetSegment(budget, theme, width, true, now);
180
- const compact = `${theme.bold("LLM")} ${compactContext} · ${compactBudget}`;
181
- if (visibleWidth(compact) <= width) return [compact];
182
- return [
183
- truncateToWidth(`${theme.bold("LLM")} ${compactContext}`, width, ""),
184
- truncateToWidth(`${theme.bold("LLM")} ${compactBudget}`, width, ""),
185
- ];
235
+ function compactUsageSegment(context: FooterContext): string {
236
+ const totals = usageTotals(context);
237
+ const parts: string[] = [];
238
+ if (totals.input) parts.push(`↑${formatTokens(totals.input)}`);
239
+ if (totals.output) parts.push(`↓${formatTokens(totals.output)}`);
240
+ return parts.join(" ");
241
+ }
242
+
243
+ function joinSegments(segments: Array<string | undefined>): string {
244
+ return segments.filter((segment): segment is string => Boolean(segment)).join(" · ");
186
245
  }
187
246
 
188
247
  export function renderFooterLines(
@@ -193,22 +252,41 @@ export function renderFooterLines(
193
252
  thinkingLevel: string,
194
253
  width: number,
195
254
  now = Date.now(),
255
+ compaction?: CompactionProgress,
196
256
  ): string[] {
197
257
  const safeWidth = Math.max(1, width);
198
- const lines = [
199
- ...identityLines(context, footerData, theme, thinkingLevel, safeWidth),
200
- ...statusLines(context, providerBudget, theme, safeWidth, now),
201
- ];
258
+ const repository = repositorySegment(context, footerData, theme);
259
+ const model = modelSegments(context, footerData, theme, thinkingLevel);
260
+ const usage = usageSegment(context);
261
+ const compactUsage = compactUsageSegment(context);
262
+ const fullContext = contextSegment(context, theme, safeWidth, false, now, compaction);
263
+ const compactContext = contextSegment(context, theme, safeWidth, true, now, compaction);
264
+ const minimalContext = minimalContextSegment(context, theme, safeWidth, now, compaction);
265
+ const fullBudget = budgetSegment(providerBudget, theme, safeWidth, false, now);
266
+ const compactBudget = budgetSegment(providerBudget, theme, safeWidth, true, now);
202
267
  const statuses = [...footerData.getExtensionStatuses().entries()]
203
268
  .filter(([key]) => key !== "jittor")
204
269
  .sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey))
205
- .map(([, text]) => sanitize(text));
206
- if (statuses.length > 0) lines.push(truncateToWidth(statuses.join(" "), safeWidth, theme.fg("dim", "…")));
207
- return lines.map((line) => truncateToWidth(line, safeWidth, ""));
270
+ .map(([, text]) => sanitize(text))
271
+ .join(" ");
272
+
273
+ const candidates = [
274
+ joinSegments([repository, model.full, usage, fullContext, fullBudget, statuses]),
275
+ joinSegments([repository, model.full, usage, fullContext, fullBudget]),
276
+ joinSegments([model.full, usage, compactContext, compactBudget, statuses]),
277
+ joinSegments([model.full, usage, compactContext, compactBudget]),
278
+ joinSegments([model.full, compactUsage, compactContext, compactBudget]),
279
+ joinSegments([model.compact, compactUsage, compactContext, compactBudget]),
280
+ joinSegments([model.compact, compactContext, compactBudget]),
281
+ joinSegments([model.compact, minimalContext, compactBudget]),
282
+ ];
283
+ const line = candidates.find((candidate) => visibleWidth(candidate) <= safeWidth) ?? candidates.at(-1) ?? "";
284
+ return [truncateToWidth(line, safeWidth, "")];
208
285
  }
209
286
 
210
287
  export interface IntegratedFooterState {
211
288
  providerBudget: ProviderBudget | null;
289
+ compaction?: CompactionProgress;
212
290
  requestRender?: () => void;
213
291
  }
214
292
 
@@ -220,7 +298,7 @@ export function installIntegratedFooter(ctx: ExtensionContext, state: Integrated
220
298
  return {
221
299
  invalidate() {},
222
300
  render(width: number): string[] {
223
- return renderFooterLines(ctx as unknown as FooterContext, footerData, theme, state.providerBudget, getThinkingLevel(), width);
301
+ return renderFooterLines(ctx as unknown as FooterContext, footerData, theme, state.providerBudget, getThinkingLevel(), width, Date.now(), state.compaction);
224
302
  },
225
303
  dispose() {
226
304
  unsubscribe?.();
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { MAX_DYNAMIC_ROUTES } from "../../src/constants.ts";
2
+ import { FOOTER_COMPACTION_RENDER_INTERVAL_MS, MAX_DYNAMIC_ROUTES } from "../../src/constants.ts";
3
3
  import type { MetricObservation, StoredMetricObservation } from "../../src/domain/metric.ts";
4
4
  import type { PolicyDecision, Route } from "../../src/policy.ts";
5
5
  import type { RouterStatus } from "../../src/ports/router-controller.ts";
@@ -32,7 +32,7 @@ async function refreshFooter(client: JittorExtensionClient, state: IntegratedFoo
32
32
  const provider = status.currentRoute?.provider;
33
33
  const query = provider === "openai-codex"
34
34
  ? { source: "codex-subscription", metric: "used-fraction", limit: 100, order: "desc" }
35
- : provider === "openrouter" ? { source: "openrouter", metric: "usage", limit: 10, order: "desc" } : null;
35
+ : provider === "openrouter" ? { source: "openrouter", limit: 20, order: "desc" } : null;
36
36
  const metrics = query ? await client.call("metrics.query", query) as StoredMetricObservation[] : [];
37
37
  state.providerBudget = buildFooterBudget(status, metrics);
38
38
  state.requestRender?.();
@@ -171,6 +171,25 @@ export function registerJittorExtension(
171
171
  enforcement: EnforcementControl = persistentEnforcementControl(),
172
172
  ): void {
173
173
  const footerState: IntegratedFooterState = { providerBudget: null };
174
+ let compactionTimer: ReturnType<typeof setInterval> | undefined;
175
+ const finishCompaction = (): void => {
176
+ if (compactionTimer) clearInterval(compactionTimer);
177
+ compactionTimer = undefined;
178
+ footerState.compaction = undefined;
179
+ footerState.requestRender?.();
180
+ };
181
+ const beginCompaction = (ctx: ExtensionContext, signal: AbortSignal): void => {
182
+ finishCompaction();
183
+ const usage = ctx.getContextUsage();
184
+ footerState.compaction = {
185
+ startedAt: Date.now(),
186
+ initialFraction: usage?.percent === null || usage?.percent === undefined ? 1 : usage.percent / 100,
187
+ };
188
+ compactionTimer = setInterval(() => footerState.requestRender?.(), FOOTER_COMPACTION_RENDER_INTERVAL_MS);
189
+ signal.addEventListener("abort", finishCompaction, { once: true });
190
+ if (signal.aborted) finishCompaction();
191
+ else footerState.requestRender?.();
192
+ };
174
193
  const showFooter = (ctx: ExtensionContext): void => {
175
194
  if (enforcement.isFooterEnabled()) installIntegratedFooter(ctx, footerState, () => pi.getThinkingLevel());
176
195
  else ctx.ui.setFooter(undefined);
@@ -201,7 +220,7 @@ export function registerJittorExtension(
201
220
  };
202
221
 
203
222
  pi.registerCommand("jittor", {
204
- description: "Inspect, enable, or disable Jittor routing",
223
+ description: "Inspect or control Jittor routing, budgets, and usage",
205
224
  handler: async (args, ctx) => {
206
225
  const action = args.trim().toLowerCase();
207
226
  if (action === "off" || action === "disable") { disable(ctx); return; }
@@ -219,6 +238,10 @@ export function registerJittorExtension(
219
238
  ctx.ui.notify("Jittor informational footer enabled; routing enforcement is unchanged.", "info");
220
239
  return;
221
240
  }
241
+ if (action === "usage") {
242
+ await showUsagePanel(ctx, client);
243
+ return;
244
+ }
222
245
  if (!enforcement.isEnabled()) {
223
246
  ctx.ui.notify("Jittor is monitor-only. Run /jittor on to re-enable blocking.", "info");
224
247
  return;
@@ -226,20 +249,9 @@ export function registerJittorExtension(
226
249
  await showJittorPanel(ctx, client);
227
250
  },
228
251
  });
229
- pi.registerCommand("jittor-off", {
230
- description: "Emergency local bypass: disable Jittor blocking without daemon access",
231
- handler: async (_args, ctx) => { disable(ctx); },
232
- });
233
- pi.registerCommand("jittor-on", {
234
- description: "Enable Jittor only after telemetry and routes pass readiness",
235
- handler: async (_args, ctx) => { await enable(ctx); },
236
- });
237
- pi.registerCommand("usage", {
238
- description: "Show Jittor token usage over time",
239
- handler: async (_args, ctx) => { await showUsagePanel(ctx, client); },
240
- });
241
252
 
242
253
  pi.on("session_start", async (_event, ctx) => {
254
+ finishCompaction();
243
255
  ctx.ui.setStatus("jittor", undefined);
244
256
  showFooter(ctx);
245
257
  try {
@@ -253,6 +265,18 @@ export function registerJittorExtension(
253
265
  }
254
266
  });
255
267
 
268
+ pi.on("session_before_compact", (event, ctx) => {
269
+ beginCompaction(ctx, event.signal);
270
+ });
271
+
272
+ pi.on("session_compact", () => {
273
+ finishCompaction();
274
+ });
275
+
276
+ pi.on("agent_settled", () => {
277
+ if (footerState.compaction) finishCompaction();
278
+ });
279
+
256
280
  pi.on("input", async (event, ctx) => {
257
281
  if (event.source === "extension" || !enforcement.isEnabled()) return { action: "continue" as const };
258
282
  try {
@@ -307,6 +331,7 @@ export function registerJittorExtension(
307
331
  });
308
332
 
309
333
  pi.on("session_shutdown", async (_event, ctx) => {
334
+ finishCompaction();
310
335
  ctx.ui.setStatus("jittor", undefined);
311
336
  ctx.ui.setFooter(undefined);
312
337
  });
@@ -15,10 +15,35 @@ function latest(rows: StoredMetricObservation[], predicate: (row: StoredMetricOb
15
15
  return rows.filter(predicate).sort((left, right) => right.observedAt - left.observedAt || right.id - left.id)[0];
16
16
  }
17
17
 
18
- function longestCodexWindow(rows: StoredMetricObservation[]): StoredMetricObservation | undefined {
19
- return rows
20
- .filter((row) => row.source === "codex-subscription" && row.metric === "used-fraction" && typeof row.value === "number")
21
- .sort((left, right) => Number(right.attributes["windowSeconds"] ?? 0) - Number(left.attributes["windowSeconds"] ?? 0) || right.observedAt - left.observedAt)[0];
18
+ function sanitizedText(value: string): string {
19
+ return value.replace(/[\r\n\t]/g, " ").replace(/ +/g, " ").trim();
20
+ }
21
+
22
+ function normalizedIdentity(value: unknown): string {
23
+ return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]+/g, "") : "";
24
+ }
25
+
26
+ function longestWindow(rows: StoredMetricObservation[]): StoredMetricObservation | undefined {
27
+ return [...rows].sort((left, right) =>
28
+ Number(right.attributes["windowSeconds"] ?? 0) - Number(left.attributes["windowSeconds"] ?? 0)
29
+ || right.observedAt - left.observedAt
30
+ || right.id - left.id,
31
+ )[0];
32
+ }
33
+
34
+ function codexWindowForModel(rows: StoredMetricObservation[], model: string): StoredMetricObservation | undefined {
35
+ const codexRows = rows.filter((row) => row.source === "codex-subscription" && row.metric === "used-fraction" && typeof row.value === "number");
36
+ const modelIdentity = normalizedIdentity(model);
37
+ const matchingAdditional = codexRows.filter((row) => {
38
+ const limitId = normalizedIdentity(row.attributes["limitId"]);
39
+ const limitName = normalizedIdentity(row.attributes["limitName"]);
40
+ return limitId !== "codex" && limitName.length > 0 && limitName === modelIdentity;
41
+ });
42
+ if (matchingAdditional.length > 0) return longestWindow(matchingAdditional);
43
+ return longestWindow(codexRows.filter((row) => {
44
+ const limitId = normalizedIdentity(row.attributes["limitId"]);
45
+ return limitId === "codex" || (limitId.length === 0 && row.scope.startsWith("codex:"));
46
+ }));
22
47
  }
23
48
 
24
49
  function compactWindowName(seconds: number): string {
@@ -36,19 +61,32 @@ function windowName(seconds: number): string {
36
61
  export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObservation[]): ProviderBudget | null {
37
62
  if (!status.ready || !status.currentRoute) return null;
38
63
  if (status.currentRoute.provider === "openai-codex") {
39
- const codex = longestCodexWindow(metrics);
64
+ const codex = codexWindowForModel(metrics, status.currentRoute.model);
40
65
  if (!codex || typeof codex.value !== "number") return null;
66
+ const resetsAtSeconds = Number(codex.attributes["resetsAt"]);
41
67
  return {
68
+ kind: "bounded",
42
69
  label: compactWindowName(Number(codex.attributes["windowSeconds"] ?? 0)),
43
- fraction: codex.value,
44
- valueText: `${(codex.value * 100).toFixed(1)}% used`,
70
+ remainingFraction: 1 - codex.value,
45
71
  observedAt: codex.observedAt,
72
+ ...(Number.isFinite(resetsAtSeconds) && resetsAtSeconds > 0 ? { resetsAt: resetsAtSeconds * 1_000 } : {}),
46
73
  };
47
74
  }
48
75
  if (status.currentRoute.provider === "openrouter") {
49
76
  const openRouter = latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number");
77
+ const remaining = latest(metrics, (row) => row.source === "openrouter" && row.metric === "remaining-fraction" && typeof row.value === "number");
78
+ if (remaining && typeof remaining.value === "number" && (!openRouter || remaining.observedAt >= openRouter.observedAt)) {
79
+ const reset = typeof remaining.attributes["reset"] === "string" ? sanitizedText(remaining.attributes["reset"]) : undefined;
80
+ return {
81
+ kind: "bounded",
82
+ label: "OR",
83
+ remainingFraction: remaining.value,
84
+ observedAt: remaining.observedAt,
85
+ ...(reset ? { resetText: `${reset} reset` } : {}),
86
+ };
87
+ }
50
88
  if (!openRouter || typeof openRouter.value !== "number") return null;
51
- return { label: "spend", fraction: null, valueText: `$${openRouter.value.toFixed(3)}`, observedAt: openRouter.observedAt };
89
+ return { kind: "unbounded", label: "spend", valueText: `$${openRouter.value.toFixed(3)}`, observedAt: openRouter.observedAt };
52
90
  }
53
91
  return null;
54
92
  }
@@ -56,7 +94,7 @@ export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObs
56
94
  export function formatFooterStatus(status: RouterStatus, metrics: StoredMetricObservation[]): string {
57
95
  const budget = buildFooterBudget(status, metrics);
58
96
  if (!budget) return "";
59
- return budget.fraction === null ? budget.valueText : `${budget.label} ${(budget.fraction * 100).toFixed(1)}%`;
97
+ return budget.kind === "unbounded" ? budget.valueText : `${budget.label} ${(budget.remainingFraction * 100).toFixed(1)}% left`;
60
98
  }
61
99
 
62
100
  function nextAction(action: PolicyAction | undefined): string {
@@ -87,10 +125,10 @@ function burnLine(rows: StoredMetricObservation[], current: StoredMetricObservat
87
125
 
88
126
  export function buildStatusView(status: RouterStatus, metrics: StoredMetricObservation[], now = Date.now()): string[] {
89
127
  const lines = [status.ready ? "Ready" : "Not ready"];
90
- const codex = status.currentRoute?.provider === "openai-codex" ? longestCodexWindow(metrics) : undefined;
128
+ const codex = status.currentRoute?.provider === "openai-codex" ? codexWindowForModel(metrics, status.currentRoute.model) : undefined;
91
129
  if (codex && typeof codex.value === "number") {
92
130
  const seconds = Number(codex.attributes["windowSeconds"] ?? 0);
93
- lines.push(`Codex ${windowName(seconds)}: ${(codex.value * 100).toFixed(1)}%`);
131
+ lines.push(`Codex ${windowName(seconds)}: ${((1 - codex.value) * 100).toFixed(1)}% left`);
94
132
  lines.push(burnLine(metrics, codex, now));
95
133
  }
96
134
  const openRouter = status.currentRoute?.provider === "openrouter"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/jittor",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Just-in-Time Token Optimizing Router for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package", "llm-router", "token-budget"],
package/src/constants.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "0.1.0";
1
+ export const VERSION = "0.1.2";
2
2
  export const SQLITE_SCHEMA_VERSION = 1;
3
3
  export const SQLITE_BUSY_TIMEOUT_MS = 5_000;
4
4
  export const DEFAULT_QUERY_LIMIT = 1_000;
@@ -13,6 +13,12 @@ export const FOOTER_CONTEXT_ERROR_FRACTION = 0.9;
13
13
  export const FOOTER_BAR_MIN_WIDTH = 4;
14
14
  export const FOOTER_BAR_MAX_WIDTH = 8;
15
15
  export const FOOTER_WIDE_TERMINAL_WIDTH = 100;
16
+ export const FOOTER_COMPACTION_RENDER_INTERVAL_MS = 1_000;
17
+ export const FOOTER_COMPACTION_DRAIN_STEP_MS = 3_000;
18
+ export const MILLISECONDS_PER_SECOND = 1_000;
19
+ export const MILLISECONDS_PER_MINUTE = 60 * MILLISECONDS_PER_SECOND;
20
+ export const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE;
21
+ export const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR;
16
22
  export const LOOPBACK_HOST = "127.0.0.1";
17
23
  export const JITTOR_STATE_DIRECTORY = "jittor";
18
24
  export const JITTOR_EXTENSION_SETTINGS_FILENAME = "extension.json";
@@ -164,7 +164,13 @@ export function parseOpenRouterKey(rootValue: unknown, observedAt: number): Open
164
164
  add("usage-monthly", snapshot.usageMonthly);
165
165
  add("limit", limit);
166
166
  add("limit-remaining", remaining);
167
- if (limit !== null && limit > 0) snapshot.metrics.push(metric(scope, "used-fraction", usage / limit, "ratio", observedAt));
167
+ if (limit !== null && limit > 0 && remaining !== null) {
168
+ const remainingFraction = remaining / limit;
169
+ if (remainingFraction < 0 || remainingFraction > 1) throw new Error("OpenRouter key remaining fraction is outside its configured limit");
170
+ const attributes = { limit, remaining, reset: snapshot.reset };
171
+ snapshot.metrics.push(metric(scope, "remaining-fraction", remainingFraction, "ratio", observedAt, attributes));
172
+ snapshot.metrics.push(metric(scope, "used-fraction", 1 - remainingFraction, "ratio", observedAt, attributes));
173
+ }
168
174
  return snapshot;
169
175
  }
170
176