@danypops/pi-jittor 0.8.0 → 0.9.0

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.
@@ -26,7 +26,6 @@ import {
26
26
  papyrusContextMetric,
27
27
  type Route,
28
28
  type RouterStatus,
29
- type StoredMetricObservation,
30
29
  TASK_DOMAINS,
31
30
  TASK_TYPES,
32
31
  type TextTokenCounter,
@@ -61,7 +60,7 @@ import {
61
60
  import { LocalRunTelemetry } from "./observability/model-run.ts";
62
61
  import { captureProviderContextSnapshot } from "./observability/provider-context-snapshot.ts";
63
62
  import { ProviderResponseTelemetry } from "./observability/provider-response.ts";
64
- import { buildFooterBudget, providerBudgetMetricQuery } from "./observability/status.ts";
63
+ import { buildFooterBudget, fetchProviderBudgetMetrics } from "./observability/status.ts";
65
64
  import { showUsagePanel } from "./observability/usage.ts";
66
65
  import { candidateIdentityOf, decideAutoMode, newAutoModeSessionState, recordAutoModeDismissal } from "./optimization/auto-mode.ts";
67
66
  import { showAutoModeSuggestion } from "./optimization/auto-mode-dialog.ts";
@@ -158,8 +157,7 @@ async function recordMetrics(client: JittorExtensionClient, metrics: MetricObser
158
157
 
159
158
  async function refreshFooter(client: JittorExtensionClient, state: IntegratedFooterState, sessionId: string): Promise<void> {
160
159
  const status = (await client.call("router.status", { session_id: sessionId })) as RouterStatus;
161
- const query = providerBudgetMetricQuery(status);
162
- const metrics = query ? ((await client.call("metrics.query", query)) as StoredMetricObservation[]) : [];
160
+ const metrics = await fetchProviderBudgetMetrics(client, status);
163
161
  state.providerBudget = buildFooterBudget(status, metrics);
164
162
  state.requestRender?.();
165
163
  }
@@ -48,7 +48,7 @@ interface FooterContext {
48
48
  }
49
49
 
50
50
  /** A bounded quota is explicitly remaining; unbounded values never receive a fabricated bar. */
51
- export type ProviderBudget =
51
+ export type ProviderBudget = { availableResets?: { count: number; observedAt: number } } & (
52
52
  | {
53
53
  kind: "bounded";
54
54
  label: string;
@@ -67,7 +67,21 @@ export type ProviderBudget =
67
67
  kind: "unavailable";
68
68
  label: string;
69
69
  valueText: string;
70
- };
70
+ }
71
+ );
72
+
73
+ export function resetAvailabilityText(budget: ProviderBudget | null | undefined, now: number): string | undefined {
74
+ const resets = budget?.availableResets;
75
+ if (
76
+ !resets ||
77
+ !Number.isSafeInteger(resets.count) ||
78
+ resets.count < 1 ||
79
+ resets.count > 10000 ||
80
+ now - resets.observedAt > TELEMETRY_STALE_AFTER_MS
81
+ )
82
+ return undefined;
83
+ return `${resets.count} reset${resets.count === 1 ? "" : "s"} available`;
84
+ }
71
85
 
72
86
  /** Compact, always-visible state of Jittor's effort-based Auto mode -- the newer, effort-driven router, distinct from the existing budget-pressure route already reflected by the model/budget segments. */
73
87
  export interface RouterFooterInfo {
@@ -227,6 +241,18 @@ function budgetSegment(
227
241
  width: number,
228
242
  compact: boolean,
229
243
  now: number,
244
+ ): string | undefined {
245
+ const quota = quotaSegment(budget, theme, width, compact, now);
246
+ const resets = resetAvailabilityText(budget, now);
247
+ return resets ? `${quota ?? "Codex"} · ${resets}` : quota;
248
+ }
249
+
250
+ function quotaSegment(
251
+ budget: ProviderBudget | null | undefined,
252
+ theme: FooterTheme,
253
+ width: number,
254
+ compact: boolean,
255
+ now: number,
230
256
  ): string | undefined {
231
257
  if (budget === undefined) return undefined;
232
258
  const w = barWidth(width);
@@ -13,7 +13,7 @@ import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tu
13
13
  import { BorderedSelectPanel, type TextMeasure } from "malevich-tui-components";
14
14
  import { sessionSecretField } from "../session-identity.ts";
15
15
  import { showConfirmDialog, showRouteOverrideMenu } from "../tui-prompts.ts";
16
- import type { ProviderBudget } from "./footer.ts";
16
+ import { type ProviderBudget, resetAvailabilityText } from "./footer.ts";
17
17
 
18
18
  export interface JittorPanelClient {
19
19
  call(operation: string, input: unknown): Promise<any>;
@@ -136,6 +136,28 @@ export function buildFooterBudget(
136
136
  metrics: StoredMetricObservation[],
137
137
  now = Date.now(),
138
138
  ): ProviderBudget | null | undefined {
139
+ const budget = buildQuotaBudget(status, metrics, now);
140
+ if (status.currentRoute?.provider !== "openai-codex" || codexTelemetryState(status, now) !== "available") return budget;
141
+ const resets = latest(
142
+ metrics,
143
+ (row) => row.source === "codex-subscription" && row.scope === "codex:resets" && row.metric === "available-resets",
144
+ );
145
+ if (
146
+ !resets ||
147
+ typeof resets.value !== "number" ||
148
+ !Number.isSafeInteger(resets.value) ||
149
+ resets.value <= 0 ||
150
+ resets.value > 10000 ||
151
+ now - resets.observedAt > TELEMETRY_STALE_AFTER_MS
152
+ )
153
+ return budget;
154
+ return {
155
+ ...(budget ?? { kind: "unavailable", label: "Codex", valueText: "usage unavailable" }),
156
+ availableResets: { count: resets.value, observedAt: resets.observedAt },
157
+ };
158
+ }
159
+
160
+ function buildQuotaBudget(status: RouterStatus, metrics: StoredMetricObservation[], now = Date.now()): ProviderBudget | null | undefined {
139
161
  if (!status.currentRoute) return null;
140
162
  if (status.currentRoute.provider === "openai-codex") {
141
163
  const codex = codexWindowForModel(metrics, status.currentRoute.model);
@@ -238,8 +260,12 @@ export function formatFooterStatus(status: RouterStatus, metrics: StoredMetricOb
238
260
  const budget = buildFooterBudget(status, metrics, now);
239
261
  if (!budget) return "";
240
262
  if (budget.kind === "unbounded") return budget.valueText;
241
- if (budget.kind === "unavailable") return `${budget.label} ${budget.valueText}`;
242
- return `${budget.label} ${(budget.remainingFraction * 100).toFixed(1)}% left`;
263
+ const resets = resetAvailabilityText(budget, now);
264
+ const quota =
265
+ budget.kind === "unavailable"
266
+ ? `${budget.label} ${budget.valueText}`
267
+ : `${budget.label} ${(budget.remainingFraction * 100).toFixed(1)}% left`;
268
+ return resets ? `${quota} · ${resets}` : quota;
243
269
  }
244
270
 
245
271
  function nextAction(action: PolicyAction | undefined): string {
@@ -287,6 +313,8 @@ export function buildStatusView(status: RouterStatus, metrics: StoredMetricObser
287
313
  const lines = [status.ready ? "Ready" : "Not ready"];
288
314
  const codex = status.currentRoute?.provider === "openai-codex" ? codexWindowForModel(metrics, status.currentRoute.model) : undefined;
289
315
  const budget = buildFooterBudget(status, metrics, now);
316
+ const resets = resetAvailabilityText(budget, now);
317
+ if (resets) lines.push(`Codex: ${resets}`);
290
318
  if (codex && typeof codex.value === "number" && budget?.kind === "bounded") {
291
319
  const seconds = Number(codex.attributes.windowSeconds ?? 0);
292
320
  lines.push(`Codex ${windowName(seconds)}: ${((1 - codex.value) * 100).toFixed(1)}% left`);
@@ -343,9 +371,24 @@ export interface StatusPanelSnapshot {
343
371
  /** Shared by the standalone status panel below and the unified /jittor shell, so both fetch identically. */
344
372
  export async function fetchStatusSnapshot(client: JittorPanelClient, sessionId: string): Promise<StatusPanelSnapshot> {
345
373
  const status = (await client.call("router.status", { session_id: sessionId })) as RouterStatus;
374
+ const metrics = await fetchProviderBudgetMetrics(client, status);
375
+ return { status, metrics };
376
+ }
377
+
378
+ export async function fetchProviderBudgetMetrics(client: JittorPanelClient, status: RouterStatus): Promise<StoredMetricObservation[]> {
346
379
  const query = providerBudgetMetricQuery(status);
347
380
  const metrics = query ? ((await client.call("metrics.query", query)) as StoredMetricObservation[]) : [];
348
- return { status, metrics };
381
+ if (status.currentRoute?.provider === "openai-codex") {
382
+ const resets = (await client.call("metrics.query", {
383
+ source: "codex-subscription",
384
+ scope: "codex:resets",
385
+ metric: "available-resets",
386
+ order: "desc",
387
+ limit: 1,
388
+ })) as StoredMetricObservation[];
389
+ return [...metrics, ...resets];
390
+ }
391
+ return metrics;
349
392
  }
350
393
 
351
394
  async function chooseOverride(ctx: ExtensionCommandContext, routes: Route[]): Promise<Route | undefined> {
@@ -15,6 +15,8 @@ let retrying: RetryingClient<JittorClient> = createRetryingClient(() => connecto
15
15
  * is classified: only reads may be transparently invoked twice after a connection-shaped error.
16
16
  */
17
17
  const OPERATION_RETRY_MODE = {
18
+ "subscription.resets.list": "retry",
19
+ "subscription.resets.redeem": "once",
18
20
  "metrics.record": "once",
19
21
  "metrics.record_batch": "once",
20
22
  "metrics.query": "retry",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-jittor",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Pi extension for Jittor token and context observability with optimization controls backed by the @danypops/jittor daemon",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "@danypops/vehicle-client": "^0.10.1",
17
17
  "@danypops/vehicle-core": "^0.17.1",
18
18
  "@danypops/vehicle-server": "^0.25.1",
19
- "@danypops/jittor": "^0.20.0",
19
+ "@danypops/jittor": "^0.22.0",
20
20
  "malevich-tui-components": "^0.28.0"
21
21
  },
22
22
  "peerDependencies": {