@praeviso/code-env-switch 0.1.5 → 0.1.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.
@@ -18,21 +18,20 @@ function resolveOutputTokens(record: Record<string, unknown>): number | null {
18
18
  record.reasoningOutputTokens,
19
19
  record.reasoning_output
20
20
  ) ?? null;
21
- if (outputTokens === null && reasoningTokens === null) return null;
22
- if (reasoningTokens === null) return outputTokens;
23
- return (outputTokens || 0) + reasoningTokens;
21
+ if (outputTokens !== null) return outputTokens;
22
+ if (reasoningTokens !== null) return reasoningTokens;
23
+ return null;
24
24
  }
25
25
 
26
- function parseCodexUsageTotalsRecord(
26
+ function splitInputTokens(
27
27
  record: Record<string, unknown>
28
- ): StatuslineUsageTotals | null {
29
- const inputTokens =
28
+ ): { inputTokens: number | null; cacheReadTokens: number | null } {
29
+ const rawInput =
30
30
  firstNumber(
31
31
  record.inputTokens,
32
32
  record.input,
33
33
  record.input_tokens
34
34
  ) ?? null;
35
- const outputTokens = resolveOutputTokens(record);
36
35
  const cacheRead =
37
36
  firstNumber(
38
37
  record.cached_input_tokens,
@@ -42,6 +41,23 @@ function parseCodexUsageTotalsRecord(
42
41
  record.cache_read,
43
42
  record.cacheRead
44
43
  ) ?? null;
44
+ if (rawInput === null) {
45
+ return { inputTokens: null, cacheReadTokens: cacheRead };
46
+ }
47
+ if (cacheRead === null) {
48
+ return { inputTokens: rawInput, cacheReadTokens: null };
49
+ }
50
+ const nonCachedInput = Math.max(0, rawInput - cacheRead);
51
+ return { inputTokens: nonCachedInput, cacheReadTokens: cacheRead };
52
+ }
53
+
54
+ function parseCodexUsageTotalsRecord(
55
+ record: Record<string, unknown>
56
+ ): StatuslineUsageTotals | null {
57
+ const split = splitInputTokens(record);
58
+ const inputTokens = split.inputTokens;
59
+ const outputTokens = resolveOutputTokens(record);
60
+ const cacheRead = split.cacheReadTokens;
45
61
  const cacheWrite =
46
62
  firstNumber(
47
63
  record.cache_creation_input_tokens,
@@ -106,22 +122,10 @@ function parseCodexInputUsageRecord(
106
122
  record.total,
107
123
  record.total_tokens
108
124
  ) ?? null;
109
- const inputTokens =
110
- firstNumber(
111
- record.inputTokens,
112
- record.input,
113
- record.input_tokens
114
- ) ?? null;
125
+ const split = splitInputTokens(record);
126
+ const inputTokens = split.inputTokens;
115
127
  const outputTokens = resolveOutputTokens(record);
116
- const cacheRead =
117
- firstNumber(
118
- record.cached_input_tokens,
119
- record.cachedInputTokens,
120
- record.cache_read_input_tokens,
121
- record.cacheReadInputTokens,
122
- record.cache_read,
123
- record.cacheRead
124
- ) ?? null;
128
+ const cacheRead = split.cacheReadTokens;
125
129
  const cacheWrite =
126
130
  firstNumber(
127
131
  record.cache_creation_input_tokens,
@@ -197,15 +201,6 @@ export function getCodexUsageTotalsFromInput(
197
201
  const parsed = parseCodexUsageTotalsRecord(totalUsage);
198
202
  if (parsed) return parsed;
199
203
  }
200
- const lastUsage = resolveNestedRecord(
201
- tokenUsage,
202
- "last_token_usage",
203
- "lastTokenUsage"
204
- );
205
- if (lastUsage) {
206
- const parsed = parseCodexUsageTotalsRecord(lastUsage);
207
- if (parsed) return parsed;
208
- }
209
204
  const parsed = parseCodexUsageTotalsRecord(tokenUsage as Record<string, unknown>);
210
205
  if (parsed) return parsed;
211
206
  }
package/src/types.ts CHANGED
@@ -101,6 +101,10 @@ export interface AddArgs {
101
101
  type: ProfileType | null;
102
102
  }
103
103
 
104
+ export interface UsageResetArgs {
105
+ yes: boolean;
106
+ }
107
+
104
108
  export type StatuslineFormat = "text" | "json";
105
109
 
106
110
  export interface StatuslineArgs {
@@ -7,6 +7,7 @@ import * as os from "os";
7
7
  import type { Config, Profile, ProfileType } from "../types";
8
8
  import { resolvePath } from "../shell/utils";
9
9
  import { normalizeType, inferProfileType, getProfileDisplayName } from "../profile/type";
10
+ import { getStatuslineDebugPath } from "../statusline/debug";
10
11
  import { calculateUsageCost, resolvePricingForProfile } from "./pricing";
11
12
 
12
13
  interface UsageRecord {
@@ -53,6 +54,17 @@ interface UsageCostIndex {
53
54
  byName: Map<string, UsageCostTotals>;
54
55
  }
55
56
 
57
+ export interface UsageCleanupFailure {
58
+ path: string;
59
+ error: string;
60
+ }
61
+
62
+ export interface UsageCleanupResult {
63
+ removed: string[];
64
+ missing: string[];
65
+ failed: UsageCleanupFailure[];
66
+ }
67
+
56
68
  interface UsageStateEntry {
57
69
  mtimeMs: number;
58
70
  size: number;
@@ -85,6 +97,8 @@ interface UsageStateFile {
85
97
  version: number;
86
98
  files: Record<string, UsageStateEntry>;
87
99
  sessions?: Record<string, UsageSessionEntry>;
100
+ usageMtimeMs?: number;
101
+ usageSize?: number;
88
102
  }
89
103
 
90
104
  interface ProfileLogEntry {
@@ -573,18 +587,26 @@ export function syncUsageFromStatuslineInput(
573
587
  let deltaCacheRead = cacheReadTokens - prevCacheRead;
574
588
  let deltaCacheWrite = cacheWriteTokens - prevCacheWrite;
575
589
  let deltaTotal = totalTokens - prevTotal;
576
- if (
577
- deltaTotal < 0 ||
578
- deltaInput < 0 ||
579
- deltaOutput < 0 ||
580
- deltaCacheRead < 0 ||
581
- deltaCacheWrite < 0
582
- ) {
590
+ if (deltaTotal < 0) {
591
+ // Session reset: treat current totals as fresh usage.
583
592
  deltaInput = inputTokens;
584
593
  deltaOutput = outputTokens;
585
594
  deltaCacheRead = cacheReadTokens;
586
595
  deltaCacheWrite = cacheWriteTokens;
587
596
  deltaTotal = totalTokens;
597
+ } else {
598
+ // Clamp negatives caused by reclassification (e.g. cache splits).
599
+ if (deltaInput < 0) deltaInput = 0;
600
+ if (deltaOutput < 0) deltaOutput = 0;
601
+ if (deltaCacheRead < 0) deltaCacheRead = 0;
602
+ if (deltaCacheWrite < 0) deltaCacheWrite = 0;
603
+ const breakdownTotal =
604
+ deltaInput + deltaOutput + deltaCacheRead + deltaCacheWrite;
605
+ if (deltaTotal === 0 && breakdownTotal === 0) {
606
+ deltaTotal = 0;
607
+ } else if (breakdownTotal > deltaTotal) {
608
+ deltaTotal = breakdownTotal;
609
+ }
588
610
  }
589
611
 
590
612
  if (deltaTotal > 0) {
@@ -618,6 +640,7 @@ export function syncUsageFromStatuslineInput(
618
640
  model: resolvedModel,
619
641
  };
620
642
  state.sessions = sessions;
643
+ updateUsageStateMetadata(state, usagePath);
621
644
  writeUsageState(statePath, state);
622
645
  } finally {
623
646
  releaseLock(lockPath, lockFd);
@@ -848,7 +871,15 @@ function readUsageState(statePath: string): UsageStateFile {
848
871
  parsed.files && typeof parsed.files === "object" ? parsed.files : {};
849
872
  const sessions =
850
873
  parsed.sessions && typeof parsed.sessions === "object" ? parsed.sessions : {};
851
- return { version: 1, files, sessions };
874
+ const usageMtimeMs = Number(parsed.usageMtimeMs);
875
+ const usageSize = Number(parsed.usageSize);
876
+ return {
877
+ version: 1,
878
+ files,
879
+ sessions,
880
+ usageMtimeMs: Number.isFinite(usageMtimeMs) ? usageMtimeMs : undefined,
881
+ usageSize: Number.isFinite(usageSize) ? usageSize : undefined,
882
+ };
852
883
  } catch {
853
884
  return { version: 1, files: {}, sessions: {} };
854
885
  }
@@ -859,7 +890,91 @@ function writeUsageState(statePath: string, state: UsageStateFile) {
859
890
  if (!fs.existsSync(dir)) {
860
891
  fs.mkdirSync(dir, { recursive: true });
861
892
  }
862
- fs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
893
+ const payload = `${JSON.stringify(state, null, 2)}\n`;
894
+ const tmpPath = `${statePath}.tmp`;
895
+ try {
896
+ fs.writeFileSync(tmpPath, payload, "utf8");
897
+ fs.renameSync(tmpPath, statePath);
898
+ } catch {
899
+ try {
900
+ if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
901
+ } catch {
902
+ // ignore cleanup failures
903
+ }
904
+ fs.writeFileSync(statePath, payload, "utf8");
905
+ }
906
+ }
907
+
908
+ function addSiblingBackupPaths(targets: Set<string>, filePath: string | null) {
909
+ if (!filePath) return;
910
+ const dir = path.dirname(filePath);
911
+ let entries: fs.Dirent[] = [];
912
+ try {
913
+ entries = fs.readdirSync(dir, { withFileTypes: true });
914
+ } catch {
915
+ return;
916
+ }
917
+ const base = path.basename(filePath);
918
+ for (const entry of entries) {
919
+ if (!entry.isFile()) continue;
920
+ if (entry.name === base) continue;
921
+ if (entry.name.startsWith(`${base}.`)) {
922
+ targets.add(path.join(dir, entry.name));
923
+ }
924
+ }
925
+ }
926
+
927
+ export function clearUsageHistory(
928
+ config: Config,
929
+ configPath: string | null
930
+ ): UsageCleanupResult {
931
+ const targets = new Set<string>();
932
+ const usagePath = getUsagePath(config, configPath);
933
+ if (usagePath) {
934
+ targets.add(usagePath);
935
+ addSiblingBackupPaths(targets, usagePath);
936
+ }
937
+ const statePath = usagePath ? getUsageStatePath(usagePath, config) : null;
938
+ if (statePath) {
939
+ targets.add(statePath);
940
+ targets.add(`${statePath}.lock`);
941
+ addSiblingBackupPaths(targets, statePath);
942
+ }
943
+ const profileLogPath = getProfileLogPath(config, configPath);
944
+ if (profileLogPath) {
945
+ targets.add(profileLogPath);
946
+ addSiblingBackupPaths(targets, profileLogPath);
947
+ }
948
+ const debugPath = getStatuslineDebugPath(configPath);
949
+ if (debugPath) {
950
+ targets.add(debugPath);
951
+ addSiblingBackupPaths(targets, debugPath);
952
+ }
953
+
954
+ const removed: string[] = [];
955
+ const missing: string[] = [];
956
+ const failed: UsageCleanupFailure[] = [];
957
+
958
+ for (const target of targets) {
959
+ if (!target) continue;
960
+ if (!fs.existsSync(target)) {
961
+ missing.push(target);
962
+ continue;
963
+ }
964
+ try {
965
+ const stat = fs.statSync(target);
966
+ if (!stat.isFile()) continue;
967
+ fs.unlinkSync(target);
968
+ removed.push(target);
969
+ } catch (err) {
970
+ failed.push({
971
+ path: target,
972
+ error: err instanceof Error ? err.message : String(err),
973
+ });
974
+ }
975
+ }
976
+
977
+ return { removed, missing, failed };
863
978
  }
864
979
 
865
980
  function collectSessionFiles(root: string | null): string[] {
@@ -969,10 +1084,12 @@ function parseCodexSessionFile(filePath: string): SessionStats {
969
1084
  let maxTotal = 0;
970
1085
  let maxInput = 0;
971
1086
  let maxOutput = 0;
1087
+ let maxCachedInput = 0;
972
1088
  let hasTotal = false;
973
1089
  let sumLast = 0;
974
1090
  let sumLastInput = 0;
975
1091
  let sumLastOutput = 0;
1092
+ let sumLastCachedInput = 0;
976
1093
  const tsRange = { start: null as string | null, end: null as string | null };
977
1094
  let cwd: string | null = null;
978
1095
  let sessionId: string | null = null;
@@ -1012,19 +1129,25 @@ function parseCodexSessionFile(filePath: string): SessionStats {
1012
1129
  if (totalTokens > maxTotal) maxTotal = totalTokens;
1013
1130
  const totalInput = Number(totalUsage.input_tokens);
1014
1131
  const totalOutput = Number(totalUsage.output_tokens);
1132
+ const totalCached = Number(totalUsage.cached_input_tokens);
1015
1133
  if (Number.isFinite(totalInput) && totalInput > maxInput) {
1016
1134
  maxInput = totalInput;
1017
1135
  }
1018
1136
  if (Number.isFinite(totalOutput) && totalOutput > maxOutput) {
1019
1137
  maxOutput = totalOutput;
1020
1138
  }
1139
+ if (Number.isFinite(totalCached) && totalCached > maxCachedInput) {
1140
+ maxCachedInput = totalCached;
1141
+ }
1021
1142
  } else {
1022
1143
  const lastTokens = Number(lastUsage.total_tokens);
1023
1144
  if (Number.isFinite(lastTokens)) sumLast += lastTokens;
1024
1145
  const lastInput = Number(lastUsage.input_tokens);
1025
1146
  const lastOutput = Number(lastUsage.output_tokens);
1147
+ const lastCached = Number(lastUsage.cached_input_tokens);
1026
1148
  if (Number.isFinite(lastInput)) sumLastInput += lastInput;
1027
1149
  if (Number.isFinite(lastOutput)) sumLastOutput += lastOutput;
1150
+ if (Number.isFinite(lastCached)) sumLastCachedInput += lastCached;
1028
1151
  }
1029
1152
  } catch {
1030
1153
  // ignore invalid lines
@@ -1035,14 +1158,21 @@ function parseCodexSessionFile(filePath: string): SessionStats {
1035
1158
  maxTotal = sumLast;
1036
1159
  maxInput = sumLastInput;
1037
1160
  maxOutput = sumLastOutput;
1161
+ maxCachedInput = sumLastCachedInput;
1038
1162
  }
1039
1163
 
1164
+ const cacheReadTokens = Math.max(0, maxCachedInput);
1165
+ const inputTokens =
1166
+ cacheReadTokens > 0 ? Math.max(0, maxInput - cacheReadTokens) : maxInput;
1167
+ const computedTotal = inputTokens + maxOutput + cacheReadTokens;
1168
+ const totalTokens = Math.max(maxTotal, computedTotal);
1169
+
1040
1170
  return {
1041
- inputTokens: maxInput,
1171
+ inputTokens,
1042
1172
  outputTokens: maxOutput,
1043
- cacheReadTokens: 0,
1173
+ cacheReadTokens,
1044
1174
  cacheWriteTokens: 0,
1045
- totalTokens: maxTotal,
1175
+ totalTokens,
1046
1176
  startTs: tsRange.start,
1047
1177
  endTs: tsRange.end,
1048
1178
  cwd,
@@ -1194,6 +1324,91 @@ function releaseLock(lockPath: string, fd: number | null) {
1194
1324
  }
1195
1325
  }
1196
1326
 
1327
+ function readUsageFileStat(usagePath: string): fs.Stats | null {
1328
+ if (!usagePath || !fs.existsSync(usagePath)) return null;
1329
+ try {
1330
+ return fs.statSync(usagePath);
1331
+ } catch {
1332
+ return null;
1333
+ }
1334
+ }
1335
+
1336
+ function buildUsageRecordKey(record: UsageRecord): string {
1337
+ return JSON.stringify([
1338
+ record.ts,
1339
+ record.type,
1340
+ record.profileKey ?? null,
1341
+ record.profileName ?? null,
1342
+ record.model ?? null,
1343
+ record.sessionId ?? null,
1344
+ toUsageNumber(record.inputTokens),
1345
+ toUsageNumber(record.outputTokens),
1346
+ toUsageNumber(record.cacheReadTokens),
1347
+ toUsageNumber(record.cacheWriteTokens),
1348
+ toUsageNumber(record.totalTokens),
1349
+ ]);
1350
+ }
1351
+
1352
+ function buildUsageSessionsFromRecords(
1353
+ records: UsageRecord[]
1354
+ ): Record<string, UsageSessionEntry> {
1355
+ const sessions: Record<string, UsageSessionEntry> = {};
1356
+ const seen = new Set<string>();
1357
+ for (const record of records) {
1358
+ if (!record.sessionId) continue;
1359
+ const normalizedType = normalizeUsageType(record.type);
1360
+ if (!normalizedType) continue;
1361
+ const recordKey = buildUsageRecordKey(record);
1362
+ if (seen.has(recordKey)) continue;
1363
+ seen.add(recordKey);
1364
+
1365
+ const sessionKey = buildSessionKey(
1366
+ normalizedType as ProfileType,
1367
+ record.sessionId
1368
+ );
1369
+ let entry = sessions[sessionKey];
1370
+ if (!entry) {
1371
+ entry = {
1372
+ type: normalizedType as ProfileType,
1373
+ inputTokens: 0,
1374
+ outputTokens: 0,
1375
+ cacheReadTokens: 0,
1376
+ cacheWriteTokens: 0,
1377
+ totalTokens: 0,
1378
+ startTs: null,
1379
+ endTs: null,
1380
+ cwd: null,
1381
+ model: record.model || null,
1382
+ };
1383
+ sessions[sessionKey] = entry;
1384
+ }
1385
+ entry.inputTokens += toUsageNumber(record.inputTokens);
1386
+ entry.outputTokens += toUsageNumber(record.outputTokens);
1387
+ entry.cacheReadTokens += toUsageNumber(record.cacheReadTokens);
1388
+ entry.cacheWriteTokens += toUsageNumber(record.cacheWriteTokens);
1389
+ entry.totalTokens += toUsageNumber(record.totalTokens);
1390
+ if (!entry.model && record.model) entry.model = record.model;
1391
+ if (record.ts) {
1392
+ const range = { start: entry.startTs, end: entry.endTs };
1393
+ updateMinMaxTs(range, record.ts);
1394
+ entry.startTs = range.start;
1395
+ entry.endTs = range.end;
1396
+ }
1397
+ }
1398
+ return sessions;
1399
+ }
1400
+
1401
+ function updateUsageStateMetadata(state: UsageStateFile, usagePath: string) {
1402
+ const stat = readUsageFileStat(usagePath);
1403
+ if (!stat || !stat.isFile()) {
1404
+ state.usageMtimeMs = undefined;
1405
+ state.usageSize = undefined;
1406
+ return;
1407
+ }
1408
+ state.usageMtimeMs = stat.mtimeMs;
1409
+ state.usageSize = stat.size;
1410
+ }
1411
+
1197
1412
  function appendUsageRecord(usagePath: string, record: UsageRecord) {
1198
1413
  const dir = path.dirname(usagePath);
1199
1414
  if (!fs.existsSync(dir)) {
@@ -1207,13 +1422,15 @@ export function readUsageRecords(usagePath: string): UsageRecord[] {
1207
1422
  const raw = fs.readFileSync(usagePath, "utf8");
1208
1423
  const lines = raw.split(/\r?\n/);
1209
1424
  const records: UsageRecord[] = [];
1425
+ const seen = new Set<string>();
1210
1426
  for (const line of lines) {
1211
1427
  const trimmed = line.trim();
1212
1428
  if (!trimmed) continue;
1213
1429
  try {
1214
1430
  const parsed = JSON.parse(trimmed);
1215
1431
  if (!parsed || typeof parsed !== "object") continue;
1216
- const input = Number(parsed.inputTokens ?? 0);
1432
+ const type = normalizeType(parsed.type) || String(parsed.type ?? "unknown");
1433
+ let input = Number(parsed.inputTokens ?? 0);
1217
1434
  const output = Number(parsed.outputTokens ?? 0);
1218
1435
  const cacheRead = Number(
1219
1436
  parsed.cacheReadTokens ??
@@ -1228,6 +1445,18 @@ export function readUsageRecords(usagePath: string): UsageRecord[] {
1228
1445
  parsed.cacheWriteInputTokens ??
1229
1446
  0
1230
1447
  );
1448
+ if (
1449
+ type === "codex" &&
1450
+ Number.isFinite(cacheRead) &&
1451
+ cacheRead > 0 &&
1452
+ Number.isFinite(input) &&
1453
+ Number.isFinite(output)
1454
+ ) {
1455
+ const rawTotal = Number(parsed.totalTokens);
1456
+ if (Number.isFinite(rawTotal) && rawTotal <= input + output) {
1457
+ input = Math.max(0, input - cacheRead);
1458
+ }
1459
+ }
1231
1460
  const computedTotal =
1232
1461
  (Number.isFinite(input) ? input : 0) +
1233
1462
  (Number.isFinite(output) ? output : 0) +
@@ -1239,7 +1468,6 @@ export function readUsageRecords(usagePath: string): UsageRecord[] {
1239
1468
  const finalTotal = Number.isFinite(total)
1240
1469
  ? Math.max(total, computedTotal)
1241
1470
  : computedTotal;
1242
- const type = normalizeType(parsed.type) || String(parsed.type ?? "unknown");
1243
1471
  const model =
1244
1472
  normalizeModelValue(parsed.model) ||
1245
1473
  normalizeModelValue(parsed.model_name) ||
@@ -1253,7 +1481,7 @@ export function readUsageRecords(usagePath: string): UsageRecord[] {
1253
1481
  parsed.sessionID ||
1254
1482
  parsed.session ||
1255
1483
  null;
1256
- records.push({
1484
+ const record: UsageRecord = {
1257
1485
  ts: String(parsed.ts ?? ""),
1258
1486
  type,
1259
1487
  profileKey: parsed.profileKey ? String(parsed.profileKey) : null,
@@ -1265,7 +1493,11 @@ export function readUsageRecords(usagePath: string): UsageRecord[] {
1265
1493
  cacheReadTokens: Number.isFinite(cacheRead) ? cacheRead : 0,
1266
1494
  cacheWriteTokens: Number.isFinite(cacheWrite) ? cacheWrite : 0,
1267
1495
  totalTokens: Number.isFinite(finalTotal) ? finalTotal : 0,
1268
- });
1496
+ };
1497
+ const key = buildUsageRecordKey(record);
1498
+ if (seen.has(key)) continue;
1499
+ seen.add(key);
1500
+ records.push(record);
1269
1501
  } catch {
1270
1502
  // ignore invalid lines
1271
1503
  }
@@ -1288,7 +1520,25 @@ export function syncUsageFromSessions(
1288
1520
 
1289
1521
  const state = readUsageState(statePath);
1290
1522
  const files = state.files || {};
1291
- const sessions = state.sessions || {};
1523
+ let sessions = state.sessions || {};
1524
+ const usageStat = readUsageFileStat(usagePath);
1525
+ const hasUsageData = !!usageStat && usageStat.isFile() && usageStat.size > 0;
1526
+ const sessionsEmpty = Object.keys(sessions).length === 0;
1527
+ const hasUsageMeta =
1528
+ Number.isFinite(state.usageMtimeMs ?? Number.NaN) &&
1529
+ Number.isFinite(state.usageSize ?? Number.NaN);
1530
+ const usageOutOfSync =
1531
+ hasUsageData &&
1532
+ (!hasUsageMeta ||
1533
+ state.usageMtimeMs !== usageStat.mtimeMs ||
1534
+ state.usageSize !== usageStat.size);
1535
+ if (hasUsageData && (sessionsEmpty || usageOutOfSync)) {
1536
+ const records = readUsageRecords(usagePath);
1537
+ if (records.length > 0) {
1538
+ sessions = buildUsageSessionsFromRecords(records);
1539
+ }
1540
+ }
1541
+ state.sessions = sessions;
1292
1542
  const codexFiles = collectSessionFiles(getCodexSessionsPath(config));
1293
1543
  const claudeFiles = collectSessionFiles(getClaudeSessionsPath(config));
1294
1544
 
@@ -1456,6 +1706,7 @@ export function syncUsageFromSessions(
1456
1706
 
1457
1707
  state.files = files;
1458
1708
  state.sessions = sessions;
1709
+ updateUsageStateMetadata(state, usagePath);
1459
1710
  writeUsageState(statePath, state);
1460
1711
  } finally {
1461
1712
  releaseLock(lockPath, lockFd);