@oh-my-pi/omp-stats 15.0.2 → 15.1.1

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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/parser.ts +39 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/omp-stats",
4
- "version": "15.0.2",
4
+ "version": "15.1.1",
5
5
  "description": "Local observability dashboard for pi AI usage statistics",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -37,8 +37,8 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/pi-ai": "15.0.2",
41
- "@oh-my-pi/pi-utils": "15.0.2",
40
+ "@oh-my-pi/pi-ai": "15.1.1",
41
+ "@oh-my-pi/pi-utils": "15.1.1",
42
42
  "@tailwindcss/node": "^4.2.4",
43
43
  "chart.js": "^4.5.1",
44
44
  "date-fns": "^4.1.0",
package/src/parser.ts CHANGED
@@ -164,9 +164,45 @@ function parseSessionEntriesLenient(bytes: Uint8Array): { entries: SessionEntry[
164
164
  return { entries, read: cursor };
165
165
  }
166
166
 
167
+ function scanLastServiceTier(bytes: Uint8Array): ServiceTier | undefined {
168
+ let cursor = 0;
169
+ let currentServiceTier: ServiceTier | undefined;
170
+
171
+ while (cursor < bytes.length) {
172
+ const { values, error, read, done } = Bun.JSONL.parseChunk(bytes, cursor, bytes.length);
173
+ for (const value of values as SessionEntry[]) {
174
+ if (isServiceTierChange(value)) currentServiceTier = value.serviceTier ?? undefined;
175
+ }
176
+
177
+ if (error) {
178
+ const nextNewline = bytes.indexOf(LF, Math.max(read, cursor));
179
+ if (nextNewline === -1) break;
180
+ cursor = nextNewline + 1;
181
+ continue;
182
+ }
183
+
184
+ if (read <= cursor) break;
185
+ cursor = read;
186
+ if (done) break;
187
+ }
188
+
189
+ return currentServiceTier;
190
+ }
167
191
  /**
168
192
  * Parse a session file and extract all assistant message stats.
169
193
  * Uses incremental reading with offset tracking.
194
+ *
195
+ * Service-tier carry-over: `currentServiceTier` is a session-scoped piece of
196
+ * state derived from `service_tier_change` entries that affects whether
197
+ * subsequent OpenAI assistant replies count as premium requests. Incremental
198
+ * syncs that resume past the most-recent tier change would otherwise lose
199
+ * that state and silently record `premiumRequests = 0` for priority traffic
200
+ * (the coding-agent stopped folding the tier into `usage.premiumRequests`
201
+ * after 13f59162e — the parser is now the sole source of truth). When
202
+ * `fromOffset > 0` we therefore scan the bytes preceding `fromOffset`
203
+ * for the latest service-tier value before parsing the unprocessed tail.
204
+ * The scan only keeps the current tier and does not materialize prefix
205
+ * entries, preserving offset-based memory behavior for large sessions.
170
206
  */
171
207
  export interface ParseSessionResult {
172
208
  stats: MessageStats[];
@@ -174,7 +210,6 @@ export interface ParseSessionResult {
174
210
  userLinks: UserMessageLink[];
175
211
  newOffset: number;
176
212
  }
177
-
178
213
  export async function parseSessionFile(sessionPath: string, fromOffset = 0): Promise<ParseSessionResult> {
179
214
  let bytes: Uint8Array;
180
215
  try {
@@ -193,6 +228,9 @@ export async function parseSessionFile(sessionPath: string, fromOffset = 0): Pro
193
228
  const unprocessed = bytes.subarray(start);
194
229
  const { entries, read } = parseSessionEntriesLenient(unprocessed);
195
230
  let currentServiceTier: ServiceTier | undefined;
231
+ if (start > 0) {
232
+ currentServiceTier = scanLastServiceTier(bytes.subarray(0, start));
233
+ }
196
234
  for (const entry of entries) {
197
235
  if (isServiceTierChange(entry)) {
198
236
  currentServiceTier = entry.serviceTier ?? undefined;