llm-usage-metrics 0.10.0 → 0.10.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.
Files changed (3) hide show
  1. package/README.md +9 -3
  2. package/dist/index.js +1501 -771
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -287,6 +287,10 @@ function createUsageEvent(input) {
287
287
  const reasoningTokens = normalizeNonNegativeInteger(input.reasoningTokens);
288
288
  const cacheReadTokens = normalizeNonNegativeInteger(input.cacheReadTokens);
289
289
  const cacheWriteTokens = normalizeNonNegativeInteger(input.cacheWriteTokens);
290
+ const cacheWrite1hTokens = Math.min(
291
+ cacheWriteTokens,
292
+ normalizeNonNegativeInteger(input.cacheWrite1hTokens)
293
+ );
290
294
  const declaredTotalTokens = normalizeNonNegativeInteger(input.totalTokens);
291
295
  const componentTotalTokens = inputTokens + outputTokens + reasoningTokens + cacheReadTokens + cacheWriteTokens;
292
296
  const totalTokens = input.totalTokens === void 0 ? componentTotalTokens : declaredTotalTokens;
@@ -304,6 +308,7 @@ function createUsageEvent(input) {
304
308
  reasoningTokens,
305
309
  cacheReadTokens,
306
310
  cacheWriteTokens,
311
+ cacheWrite1hTokens,
307
312
  totalTokens,
308
313
  costUsd,
309
314
  costMode
@@ -555,6 +560,7 @@ function slowNormalizeStoredEvent(row) {
555
560
  reasoningTokens: toNonNegativeInteger(row.reasoning_tokens),
556
561
  cacheReadTokens: toNonNegativeInteger(row.cache_read_tokens),
557
562
  cacheWriteTokens: toNonNegativeInteger(row.cache_write_tokens),
563
+ cacheWrite1hTokens: toNonNegativeInteger(row.cache_write_1h_tokens),
558
564
  totalTokens: toNonNegativeInteger(row.total_tokens),
559
565
  costUsd: toNonNegativeNumber(row.cost_usd),
560
566
  costMode
@@ -578,6 +584,7 @@ function fastMaterializeStoredEvent(row) {
578
584
  row.reasoning_tokens,
579
585
  row.cache_read_tokens,
580
586
  row.cache_write_tokens,
587
+ row.cache_write_1h_tokens,
581
588
  row.total_tokens,
582
589
  row.cost_usd,
583
590
  row.cost_mode
@@ -600,12 +607,13 @@ function normalizeStoredEventTuple(row) {
600
607
  reasoning_tokens: row[8],
601
608
  cache_read_tokens: row[9],
602
609
  cache_write_tokens: row[10],
603
- total_tokens: row[11],
604
- cost_usd: row[12],
605
- cost_mode: row[13]
610
+ cache_write_1h_tokens: row[11],
611
+ total_tokens: row[12],
612
+ cost_usd: row[13],
613
+ cost_mode: row[14]
606
614
  });
607
615
  }
608
- function materializeStoredEvent(sourceValue, sessionIdValue, timestampValue, modelValue, providerValue, repoRootValue, inputTokensValue, outputTokensValue, reasoningTokensValue, cacheReadTokensValue, cacheWriteTokensValue, totalTokensValue, costUsdValue, costModeValue) {
616
+ function materializeStoredEvent(sourceValue, sessionIdValue, timestampValue, modelValue, providerValue, repoRootValue, inputTokensValue, outputTokensValue, reasoningTokensValue, cacheReadTokensValue, cacheWriteTokensValue, cacheWrite1hTokensValue, totalTokensValue, costUsdValue, costModeValue) {
609
617
  const costMode = costModeValue === "explicit" || costModeValue === "estimated" ? costModeValue : void 0;
610
618
  if (!costMode) {
611
619
  return void 0;
@@ -621,9 +629,10 @@ function materializeStoredEvent(sourceValue, sessionIdValue, timestampValue, mod
621
629
  const reasoningTokens = toStoredNonNegativeInteger(reasoningTokensValue);
622
630
  const cacheReadTokens = toStoredNonNegativeInteger(cacheReadTokensValue);
623
631
  const cacheWriteTokens = toStoredNonNegativeInteger(cacheWriteTokensValue);
632
+ const cacheWrite1hTokens = toStoredNonNegativeInteger(cacheWrite1hTokensValue);
624
633
  const totalTokens = toStoredNonNegativeInteger(totalTokensValue);
625
634
  const costUsd = toStoredCostUsd(costUsdValue, costMode);
626
- if (!source || !sessionId || !timestamp || repoRoot === FAST_PATH_REJECT || provider === FAST_PATH_REJECT || model === FAST_PATH_REJECT || inputTokens === void 0 || outputTokens === void 0 || reasoningTokens === void 0 || cacheReadTokens === void 0 || cacheWriteTokens === void 0 || totalTokens === void 0 || costUsd === FAST_PATH_REJECT) {
635
+ if (!source || !sessionId || !timestamp || repoRoot === FAST_PATH_REJECT || provider === FAST_PATH_REJECT || model === FAST_PATH_REJECT || inputTokens === void 0 || outputTokens === void 0 || reasoningTokens === void 0 || cacheReadTokens === void 0 || cacheWriteTokens === void 0 || cacheWrite1hTokens === void 0 || cacheWrite1hTokens > cacheWriteTokens || totalTokens === void 0 || costUsd === FAST_PATH_REJECT) {
627
636
  return void 0;
628
637
  }
629
638
  if (provider && normalizeProviderToBillingEntity(provider) !== provider) {
@@ -644,6 +653,7 @@ function materializeStoredEvent(sourceValue, sessionIdValue, timestampValue, mod
644
653
  reasoningTokens,
645
654
  cacheReadTokens,
646
655
  cacheWriteTokens,
656
+ cacheWrite1hTokens,
647
657
  totalTokens,
648
658
  costUsd,
649
659
  costMode
@@ -717,6 +727,8 @@ function computeEventContentHash(event) {
717
727
  event.reasoningTokens,
718
728
  event.cacheReadTokens,
719
729
  event.cacheWriteTokens,
730
+ // cacheWrite1hTokens is left out: it only splits cacheWriteTokens, and leaving it out
731
+ // keeps events stored before it existed matching their reparsed copies.
720
732
  event.totalTokens,
721
733
  event.costMode,
722
734
  event.costUsd ?? ""
@@ -724,8 +736,367 @@ function computeEventContentHash(event) {
724
736
  return createHash("sha256").update(fields.map(String).join("")).digest("hex").slice(0, 16);
725
737
  }
726
738
 
739
+ // src/persistence/event-store-history.ts
740
+ function toPositiveCount(value) {
741
+ return toNonNegativeInteger(value) ?? 0;
742
+ }
743
+ function normalizeHistorySource(source) {
744
+ return normalizeSourceId(source)?.toLowerCase();
745
+ }
746
+ function normalizeHistoryFilePath(filePath) {
747
+ const normalized = filePath.trim();
748
+ return normalized || void 0;
749
+ }
750
+ function createTempTables(store) {
751
+ store.database.exec(`
752
+ CREATE TEMP TABLE IF NOT EXISTS history_selected_sources (
753
+ source TEXT PRIMARY KEY,
754
+ repeats_events INTEGER NOT NULL
755
+ );
756
+ CREATE TEMP TABLE IF NOT EXISTS history_discovered_files (
757
+ source TEXT NOT NULL,
758
+ file_path TEXT NOT NULL,
759
+ counted INTEGER NOT NULL,
760
+ PRIMARY KEY (source, file_path)
761
+ );
762
+ CREATE TEMP TABLE IF NOT EXISTS history_departed_files (
763
+ source TEXT NOT NULL,
764
+ file_path TEXT NOT NULL,
765
+ PRIMARY KEY (source, file_path)
766
+ );
767
+ CREATE TEMP TABLE IF NOT EXISTS history_served_files (
768
+ source TEXT NOT NULL,
769
+ file_path TEXT NOT NULL,
770
+ ordinal INTEGER NOT NULL,
771
+ PRIMARY KEY (source, file_path)
772
+ );
773
+ DELETE FROM history_selected_sources;
774
+ DELETE FROM history_discovered_files;
775
+ DELETE FROM history_departed_files;
776
+ DELETE FROM history_served_files;
777
+ `);
778
+ }
779
+ function writeTempInputs(store, input) {
780
+ const selectedSources = /* @__PURE__ */ new Set();
781
+ const insertSelectedSource = store.database.prepare(
782
+ "INSERT OR IGNORE INTO history_selected_sources (source, repeats_events) VALUES (?, ?)"
783
+ );
784
+ const repeatingSources = new Set(
785
+ (input.repeatingSources ?? []).map((source) => normalizeHistorySource(source))
786
+ );
787
+ const insertDiscoveredFile = store.database.prepare(
788
+ [
789
+ "INSERT OR IGNORE INTO history_discovered_files (source, file_path, counted)",
790
+ "VALUES (?, ?, ?)"
791
+ ].join("\n")
792
+ );
793
+ for (const source of input.selectedSources) {
794
+ const normalizedSource = normalizeHistorySource(source);
795
+ if (!normalizedSource) {
796
+ continue;
797
+ }
798
+ selectedSources.add(normalizedSource);
799
+ insertSelectedSource.run(normalizedSource, repeatingSources.has(normalizedSource) ? 1 : 0);
800
+ }
801
+ const insertLiveFiles = (files, counted) => {
802
+ for (const file of files) {
803
+ const normalizedSource = normalizeHistorySource(file.source);
804
+ const normalizedFilePath = normalizeHistoryFilePath(file.filePath);
805
+ if (!normalizedSource || !normalizedFilePath || !selectedSources.has(normalizedSource)) {
806
+ continue;
807
+ }
808
+ insertDiscoveredFile.run(normalizedSource, normalizedFilePath, counted ? 1 : 0);
809
+ }
810
+ };
811
+ insertLiveFiles(input.discoveredFiles, true);
812
+ insertLiveFiles(input.presentFiles ?? [], false);
813
+ return selectedSources;
814
+ }
815
+ function historyFileKey(source, filePath) {
816
+ return `${source}\0${filePath}`;
817
+ }
818
+ function readUndiscoveredStoredFiles(store, input) {
819
+ const selectedSources = /* @__PURE__ */ new Set();
820
+ for (const source of input.selectedSources) {
821
+ const normalizedSource = normalizeHistorySource(source);
822
+ if (normalizedSource) {
823
+ selectedSources.add(normalizedSource);
824
+ }
825
+ }
826
+ const discoveredKeys = /* @__PURE__ */ new Set();
827
+ for (const discoveredFile of [...input.discoveredFiles, ...input.presentFiles ?? []]) {
828
+ const normalizedSource = normalizeHistorySource(discoveredFile.source);
829
+ const normalizedFilePath = normalizeHistoryFilePath(discoveredFile.filePath);
830
+ if (normalizedSource && normalizedFilePath) {
831
+ discoveredKeys.add(historyFileKey(normalizedSource, normalizedFilePath));
832
+ }
833
+ }
834
+ const rows = store.database.prepare("SELECT source, file_path FROM files ORDER BY source ASC, file_path ASC").all();
835
+ const undiscoveredFiles = [];
836
+ for (const row of rows) {
837
+ const source = toText(row.source);
838
+ const filePath = toText(row.file_path);
839
+ if (source && filePath && selectedSources.has(source) && !discoveredKeys.has(historyFileKey(source, filePath))) {
840
+ undiscoveredFiles.push({ source, filePath });
841
+ }
842
+ }
843
+ return undiscoveredFiles;
844
+ }
845
+ function addHashCount(target, hash, count) {
846
+ if (count <= 0) {
847
+ return;
848
+ }
849
+ target.set(hash, (target.get(hash) ?? 0) + count);
850
+ }
851
+ function readLiveHashCounts(store) {
852
+ const rows = store.database.prepare(
853
+ [
854
+ "SELECT content_hash,",
855
+ " CASE WHEN MAX(repeats_events) = 1 THEN MAX(file_count) ELSE SUM(file_count) END",
856
+ " AS count",
857
+ "FROM (",
858
+ " SELECT events.content_hash AS content_hash,",
859
+ " selected.repeats_events AS repeats_events, COUNT(*) AS file_count",
860
+ " FROM history_discovered_files AS discovered",
861
+ " JOIN history_selected_sources AS selected",
862
+ " ON discovered.source = selected.source",
863
+ " CROSS JOIN events",
864
+ " WHERE discovered.counted = 1",
865
+ " AND events.source = discovered.source",
866
+ " AND events.file_path = discovered.file_path",
867
+ " AND events.content_hash IS NOT NULL",
868
+ " GROUP BY discovered.source, discovered.file_path, events.content_hash",
869
+ ")",
870
+ "GROUP BY content_hash"
871
+ ].join("\n")
872
+ ).all();
873
+ const counts = /* @__PURE__ */ new Map();
874
+ for (const row of rows) {
875
+ const hash = toText(row.content_hash);
876
+ if (!hash) {
877
+ continue;
878
+ }
879
+ addHashCount(counts, hash, toPositiveCount(row.count));
880
+ }
881
+ return counts;
882
+ }
883
+ function compareDepartedFiles(left, right4) {
884
+ if (left.ingestedAt !== right4.ingestedAt) {
885
+ return left.ingestedAt - right4.ingestedAt;
886
+ }
887
+ if (left.filePath !== right4.filePath) {
888
+ return compareByCodePoint(left.filePath, right4.filePath);
889
+ }
890
+ return compareByCodePoint(left.source, right4.source);
891
+ }
892
+ function readDepartedFiles(store) {
893
+ const rows = store.database.prepare(
894
+ [
895
+ "SELECT files.source, files.file_path, files.ingested_at,",
896
+ " COUNT(events.id) AS event_count, MAX(events.timestamp) AS newest_timestamp",
897
+ "FROM files",
898
+ "JOIN history_selected_sources AS selected",
899
+ " ON files.source = selected.source",
900
+ "LEFT JOIN history_discovered_files AS discovered",
901
+ " ON files.source = discovered.source",
902
+ " AND files.file_path = discovered.file_path",
903
+ "LEFT JOIN events",
904
+ " ON files.source = events.source",
905
+ " AND files.file_path = events.file_path",
906
+ "WHERE discovered.file_path IS NULL",
907
+ "GROUP BY files.source, files.file_path, files.ingested_at"
908
+ ].join("\n")
909
+ ).all();
910
+ const departedFiles = [];
911
+ for (const row of rows) {
912
+ const source = toText(row.source);
913
+ const filePath = toText(row.file_path);
914
+ const ingestedAt = toNonNegativeInteger(row.ingested_at);
915
+ const eventCount = toNonNegativeInteger(row.event_count);
916
+ const newestTimestamp = toText(row.newest_timestamp);
917
+ if (!source || !filePath || ingestedAt === void 0 || eventCount === void 0) {
918
+ continue;
919
+ }
920
+ departedFiles.push({ source, filePath, ingestedAt, eventCount, newestTimestamp });
921
+ }
922
+ return departedFiles.sort(compareDepartedFiles);
923
+ }
924
+ function fileKey(source, filePath) {
925
+ return JSON.stringify([source, filePath]);
926
+ }
927
+ function readDepartedHashMultisets(store) {
928
+ store.database.prepare(
929
+ [
930
+ "INSERT INTO history_departed_files (source, file_path)",
931
+ "SELECT files.source, files.file_path",
932
+ "FROM files",
933
+ "JOIN history_selected_sources AS selected",
934
+ " ON files.source = selected.source",
935
+ "LEFT JOIN history_discovered_files AS discovered",
936
+ " ON files.source = discovered.source",
937
+ " AND files.file_path = discovered.file_path",
938
+ "WHERE discovered.file_path IS NULL"
939
+ ].join("\n")
940
+ ).run();
941
+ const rows = store.database.prepare(
942
+ [
943
+ "SELECT events.source AS source, events.file_path AS file_path,",
944
+ " events.content_hash AS content_hash, COUNT(*) AS count",
945
+ "FROM history_departed_files AS departed",
946
+ "CROSS JOIN events",
947
+ "WHERE events.source = departed.source",
948
+ " AND events.file_path = departed.file_path",
949
+ "GROUP BY events.source, events.file_path, events.content_hash"
950
+ ].join("\n")
951
+ ).all();
952
+ const multisets = /* @__PURE__ */ new Map();
953
+ for (const row of rows) {
954
+ const source = toText(row.source);
955
+ const filePath = toText(row.file_path);
956
+ if (!source || !filePath) {
957
+ continue;
958
+ }
959
+ const key = fileKey(source, filePath);
960
+ let multiset = multisets.get(key);
961
+ if (!multiset) {
962
+ multiset = { counts: /* @__PURE__ */ new Map(), hasNullHash: false };
963
+ multisets.set(key, multiset);
964
+ }
965
+ const hash = toText(row.content_hash);
966
+ if (!hash) {
967
+ multiset.hasNullHash = true;
968
+ continue;
969
+ }
970
+ addHashCount(multiset.counts, hash, toPositiveCount(row.count));
971
+ }
972
+ return multisets;
973
+ }
974
+ function isSubsetOfServedData(fileHashCounts, servedHashCounts) {
975
+ if (fileHashCounts.hasNullHash) {
976
+ return false;
977
+ }
978
+ for (const [hash, count] of fileHashCounts.counts.entries()) {
979
+ if (count > (servedHashCounts.get(hash) ?? 0)) {
980
+ return false;
981
+ }
982
+ }
983
+ return true;
984
+ }
985
+ function addServedFileHashCounts(servedHashCounts, fileHashCounts) {
986
+ for (const [hash, count] of fileHashCounts.counts.entries()) {
987
+ servedHashCounts.set(hash, Math.max(count, servedHashCounts.get(hash) ?? 0));
988
+ }
989
+ }
990
+ function takeUncountedEvents(events, countedHashes) {
991
+ const fileHashCounts = /* @__PURE__ */ new Map();
992
+ const keptHashes = [];
993
+ const keptEvents = events.filter((event) => {
994
+ const hash = computeEventContentHash(event);
995
+ const seen = (fileHashCounts.get(hash) ?? 0) + 1;
996
+ fileHashCounts.set(hash, seen);
997
+ if (seen <= (countedHashes.get(hash) ?? 0)) {
998
+ return false;
999
+ }
1000
+ keptHashes.push(hash);
1001
+ return true;
1002
+ });
1003
+ for (const hash of keptHashes) {
1004
+ addHashCount(countedHashes, hash, 1);
1005
+ }
1006
+ return keptEvents;
1007
+ }
1008
+ function loadServedEvents(store, servedFiles, liveHashCounts) {
1009
+ if (servedFiles.length === 0) {
1010
+ return [];
1011
+ }
1012
+ const INSERT_CHUNK_FILES = 500;
1013
+ for (let start = 0; start < servedFiles.length; start += INSERT_CHUNK_FILES) {
1014
+ const chunk = servedFiles.slice(start, start + INSERT_CHUNK_FILES);
1015
+ const placeholders = chunk.map(() => "(?, ?, ?)").join(", ");
1016
+ const params = [];
1017
+ chunk.forEach((file, index) => {
1018
+ params.push(file.source, file.filePath, start + index);
1019
+ });
1020
+ store.database.prepare(
1021
+ `INSERT INTO history_served_files (source, file_path, ordinal) VALUES ${placeholders}`
1022
+ ).run(...params);
1023
+ }
1024
+ const rows = store.database.prepare(
1025
+ [
1026
+ "SELECT events.source, events.session_id, events.timestamp, events.model,",
1027
+ " events.provider, events.repo_root, events.input_tokens, events.output_tokens,",
1028
+ " events.reasoning_tokens, events.cache_read_tokens, events.cache_write_tokens,",
1029
+ " events.cache_write_1h_tokens, events.total_tokens, events.cost_usd, events.cost_mode,",
1030
+ " served.ordinal",
1031
+ "FROM history_served_files AS served",
1032
+ "CROSS JOIN events",
1033
+ "WHERE events.source = served.source",
1034
+ " AND events.file_path = served.file_path",
1035
+ "ORDER BY served.ordinal ASC, events.event_index ASC"
1036
+ ].join("\n")
1037
+ ).all();
1038
+ const eventsByFile = servedFiles.map(() => []);
1039
+ for (const row of rows) {
1040
+ const event = normalizeStoredEvent(row);
1041
+ const ordinal = toNonNegativeInteger(row.ordinal);
1042
+ if (!event || ordinal === void 0) {
1043
+ continue;
1044
+ }
1045
+ eventsByFile[ordinal].push(event);
1046
+ }
1047
+ const countedHashes = new Map(liveHashCounts);
1048
+ return eventsByFile.flatMap((fileEvents) => takeUncountedEvents(fileEvents, countedHashes));
1049
+ }
1050
+ function classifyDepartedFiles(store, input) {
1051
+ return classifyAgainstLiveFiles(store, input).classifiedFiles;
1052
+ }
1053
+ function classifyAgainstLiveFiles(store, input) {
1054
+ createTempTables(store);
1055
+ const selectedSources = writeTempInputs(store, input);
1056
+ if (selectedSources.size === 0) {
1057
+ return { classifiedFiles: [], liveHashCounts: /* @__PURE__ */ new Map() };
1058
+ }
1059
+ const liveHashCounts = readLiveHashCounts(store);
1060
+ const servedHashCounts = new Map(liveHashCounts);
1061
+ const departedFiles = readDepartedFiles(store);
1062
+ const departedHashMultisets = readDepartedHashMultisets(store);
1063
+ const classifiedFiles = [];
1064
+ for (const departedFile of departedFiles) {
1065
+ const fileHashCounts = departedHashMultisets.get(
1066
+ fileKey(departedFile.source, departedFile.filePath)
1067
+ ) ?? { counts: /* @__PURE__ */ new Map(), hasNullHash: false };
1068
+ const suppressed = isSubsetOfServedData(fileHashCounts, servedHashCounts);
1069
+ classifiedFiles.push({
1070
+ source: departedFile.source,
1071
+ filePath: departedFile.filePath,
1072
+ eventCount: departedFile.eventCount,
1073
+ newestTimestamp: departedFile.newestTimestamp,
1074
+ suppressed
1075
+ });
1076
+ if (suppressed) {
1077
+ continue;
1078
+ }
1079
+ addServedFileHashCounts(servedHashCounts, fileHashCounts);
1080
+ }
1081
+ return { classifiedFiles, liveHashCounts };
1082
+ }
1083
+ function loadHistoryEvents(store, input) {
1084
+ const { classifiedFiles: departedFiles, liveHashCounts } = classifyAgainstLiveFiles(store, input);
1085
+ const servedFiles = departedFiles.filter((file) => !file.suppressed);
1086
+ const events = loadServedEvents(store, servedFiles, liveHashCounts);
1087
+ const suppressedFileCount = departedFiles.length - servedFiles.length;
1088
+ return {
1089
+ events,
1090
+ servedFiles: servedFiles.map(({ source, filePath }) => ({ source, filePath })),
1091
+ departedFileCount: departedFiles.length,
1092
+ servedFileCount: servedFiles.length,
1093
+ suppressedFileCount,
1094
+ servedEventCount: events.length
1095
+ };
1096
+ }
1097
+
727
1098
  // src/persistence/event-store-schema.ts
728
- var EVENT_STORE_SCHEMA_VERSION = "3";
1099
+ var EVENT_STORE_SCHEMA_VERSION = "4";
729
1100
  var MIGRATION_BATCH_SIZE = 5e3;
730
1101
  var EventStoreSchemaVersionError = class extends Error {
731
1102
  schemaVersion;
@@ -768,6 +1139,7 @@ CREATE TABLE IF NOT EXISTS events (
768
1139
  reasoning_tokens INTEGER NOT NULL,
769
1140
  cache_read_tokens INTEGER NOT NULL,
770
1141
  cache_write_tokens INTEGER NOT NULL,
1142
+ cache_write_1h_tokens INTEGER NOT NULL DEFAULT 0,
771
1143
  total_tokens INTEGER NOT NULL,
772
1144
  content_hash TEXT,
773
1145
  cost_usd REAL,
@@ -809,7 +1181,7 @@ function migrateSchemaV1ToV2(database) {
809
1181
  [
810
1182
  "SELECT id, source, session_id, timestamp, model, provider, repo_root,",
811
1183
  " input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,",
812
- " cache_write_tokens, total_tokens, cost_usd, cost_mode",
1184
+ " cache_write_tokens, 0 AS cache_write_1h_tokens, total_tokens, cost_usd, cost_mode",
813
1185
  "FROM events",
814
1186
  "WHERE id > ?",
815
1187
  "ORDER BY id ASC",
@@ -849,7 +1221,7 @@ function migrateSchemaV2ToV3(database) {
849
1221
  [
850
1222
  "SELECT id, source, session_id, timestamp, model, provider, repo_root,",
851
1223
  " input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,",
852
- " cache_write_tokens, total_tokens, cost_usd, cost_mode",
1224
+ " cache_write_tokens, 0 AS cache_write_1h_tokens, total_tokens, cost_usd, cost_mode",
853
1225
  "FROM events",
854
1226
  "WHERE id > ?",
855
1227
  "ORDER BY id ASC",
@@ -876,8 +1248,17 @@ function migrateSchemaV2ToV3(database) {
876
1248
  database.prepare("UPDATE meta SET value = ? WHERE key = 'schemaVersion'").run("3");
877
1249
  });
878
1250
  }
1251
+ function migrateSchemaV3ToV4(database) {
1252
+ runTransaction(database, () => {
1253
+ if (readSchemaVersion(database) !== "3") {
1254
+ return;
1255
+ }
1256
+ database.exec("ALTER TABLE events ADD COLUMN cache_write_1h_tokens INTEGER NOT NULL DEFAULT 0");
1257
+ database.prepare("UPDATE meta SET value = ? WHERE key = 'schemaVersion'").run("4");
1258
+ });
1259
+ }
879
1260
  function isSupportedSchemaVersion(schemaVersion) {
880
- return schemaVersion === EVENT_STORE_SCHEMA_VERSION || schemaVersion === "1" || schemaVersion === "2";
1261
+ return schemaVersion === EVENT_STORE_SCHEMA_VERSION || schemaVersion === "1" || schemaVersion === "2" || schemaVersion === "3";
881
1262
  }
882
1263
  function assertSupportedSchemaVersion(database) {
883
1264
  const schemaVersion = readSchemaVersion(database);
@@ -905,10 +1286,16 @@ function initializeSchema(database) {
905
1286
  if (schemaVersion === "1") {
906
1287
  migrateSchemaV1ToV2(database);
907
1288
  migrateSchemaV2ToV3(database);
1289
+ migrateSchemaV3ToV4(database);
908
1290
  return;
909
1291
  }
910
1292
  if (schemaVersion === "2") {
911
1293
  migrateSchemaV2ToV3(database);
1294
+ migrateSchemaV3ToV4(database);
1295
+ return;
1296
+ }
1297
+ if (schemaVersion === "3") {
1298
+ migrateSchemaV3ToV4(database);
912
1299
  return;
913
1300
  }
914
1301
  throw new EventStoreSchemaVersionError(schemaVersion);
@@ -1226,11 +1613,13 @@ async function readEventStoreEvents(filePath, window = {}, loadSqliteModule = lo
1226
1613
  });
1227
1614
  try {
1228
1615
  assertSupportedSchemaVersion(database);
1616
+ const meta = readMetaValues(database);
1617
+ const cacheWrite1hColumn = meta.get("schemaVersion") === EVENT_STORE_SCHEMA_VERSION ? "cache_write_1h_tokens" : "0 AS cache_write_1h_tokens";
1229
1618
  const statement = database.prepare(
1230
1619
  [
1231
1620
  "SELECT source, session_id, timestamp, model, provider, repo_root,",
1232
1621
  " input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,",
1233
- " cache_write_tokens, total_tokens, cost_usd, cost_mode",
1622
+ ` cache_write_tokens, ${cacheWrite1hColumn}, total_tokens, cost_usd, cost_mode`,
1234
1623
  "FROM events",
1235
1624
  "WHERE timestamp >= ? AND timestamp < ?",
1236
1625
  "ORDER BY source, file_path, event_index"
@@ -1248,7 +1637,7 @@ async function readEventStoreEvents(filePath, window = {}, loadSqliteModule = lo
1248
1637
  events.push(event);
1249
1638
  }
1250
1639
  }
1251
- return { meta: readMetaValues(database), events };
1640
+ return { meta, events };
1252
1641
  } finally {
1253
1642
  database.close();
1254
1643
  }
@@ -1289,7 +1678,7 @@ function selectFileEventRows(store, source, filePath) {
1289
1678
  [
1290
1679
  "SELECT source, session_id, timestamp, model, provider, repo_root,",
1291
1680
  " input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,",
1292
- " cache_write_tokens, total_tokens, cost_usd, cost_mode",
1681
+ " cache_write_tokens, cache_write_1h_tokens, total_tokens, cost_usd, cost_mode",
1293
1682
  "FROM events",
1294
1683
  "WHERE source = ? AND file_path = ?",
1295
1684
  "ORDER BY event_index ASC"
@@ -1327,21 +1716,41 @@ function readDepartedFileEvents(store, source, filePath) {
1327
1716
  }
1328
1717
  return events;
1329
1718
  }
1330
- function readStoredFileSnapshots(store, files) {
1719
+ function readStoredFileSnapshots(store, files, options = {}) {
1720
+ const historyFiles = options.historyFiles ?? [];
1721
+ const repeatingSources = options.repeatingSources ?? /* @__PURE__ */ new Set();
1331
1722
  const snapshots = [];
1723
+ const countedHashes = /* @__PURE__ */ new Map();
1724
+ const readSnapshot = (file, selectEvents) => {
1725
+ const source = normalizeStoreSource(file.source);
1726
+ const filePath = normalizeStoreFilePath(file.filePath);
1727
+ const events = selectEvents(readDepartedFileEvents(store, source, filePath));
1728
+ snapshots.push({
1729
+ source,
1730
+ filePath,
1731
+ revision: createHash2("sha256").update(JSON.stringify(events)).digest("hex").slice(0, 16),
1732
+ events
1733
+ });
1734
+ };
1332
1735
  store.database.exec("BEGIN");
1333
1736
  try {
1334
1737
  for (const file of files) {
1335
- const source = normalizeStoreSource(file.source);
1336
- const filePath = normalizeStoreFilePath(file.filePath);
1337
- const events = readDepartedFileEvents(store, source, filePath);
1338
- snapshots.push({
1339
- source,
1340
- filePath,
1341
- revision: createHash2("sha256").update(JSON.stringify(events)).digest("hex").slice(0, 16),
1342
- events
1738
+ readSnapshot(file, (events) => {
1739
+ if (repeatingSources.has(normalizeStoreSource(file.source))) {
1740
+ return takeUncountedEvents(events, countedHashes);
1741
+ }
1742
+ if (historyFiles.length > 0) {
1743
+ for (const event of events) {
1744
+ const hash = computeEventContentHash(event);
1745
+ countedHashes.set(hash, (countedHashes.get(hash) ?? 0) + 1);
1746
+ }
1747
+ }
1748
+ return events;
1343
1749
  });
1344
1750
  }
1751
+ for (const file of historyFiles) {
1752
+ readSnapshot(file, (events) => takeUncountedEvents(events, countedHashes));
1753
+ }
1345
1754
  } finally {
1346
1755
  store.database.exec("COMMIT");
1347
1756
  }
@@ -1400,8 +1809,9 @@ function replaceFilesEvents(store, inputs) {
1400
1809
  "INSERT INTO events (",
1401
1810
  " source, file_path, event_index, session_id, timestamp, model, provider, repo_root,",
1402
1811
  " input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,",
1403
- " cache_write_tokens, total_tokens, content_hash, cost_usd, cost_mode",
1404
- ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
1812
+ " cache_write_tokens, cache_write_1h_tokens, total_tokens, content_hash, cost_usd,",
1813
+ " cost_mode",
1814
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
1405
1815
  ].join("\n")
1406
1816
  );
1407
1817
  const upsertFile = store.statements.upsertFile ??= store.database.prepare(
@@ -1435,6 +1845,7 @@ function replaceFilesEvents(store, inputs) {
1435
1845
  event.reasoningTokens,
1436
1846
  event.cacheReadTokens,
1437
1847
  event.cacheWriteTokens,
1848
+ event.cacheWrite1hTokens,
1438
1849
  event.totalTokens,
1439
1850
  computeEventContentHash(event),
1440
1851
  event.costUsd ?? null,
@@ -4413,6 +4824,15 @@ async function readBoundedJsonFile(filePath) {
4413
4824
  return { ok: false, reason: "json_parse_error" };
4414
4825
  }
4415
4826
  }
4827
+ async function readJsonTranscriptFile(filePath) {
4828
+ const readResult = await readBoundedJsonFile(filePath);
4829
+ if (!readResult.ok) {
4830
+ throw new Error(
4831
+ readResult.reason === "file_too_large" ? `Transcript is larger than ${String(MAX_JSON_TRANSCRIPT_BYTES / 1024 / 1024)} MiB: ${filePath}` : `Transcript is not readable JSON: ${filePath}`
4832
+ );
4833
+ }
4834
+ return readResult.value;
4835
+ }
4416
4836
 
4417
4837
  // src/sources/amp/amp-source-adapter.ts
4418
4838
  function normalizeEnvPath(value) {
@@ -4601,6 +5021,7 @@ function parseMessageUsage(context) {
4601
5021
  }
4602
5022
  var AmpSourceAdapter = class {
4603
5023
  id = "amp";
5024
+ parserVersion = 2;
4604
5025
  capabilities = { eventsPrecedeFileMtime: true };
4605
5026
  threadsDir;
4606
5027
  requireDir;
@@ -4633,42 +5054,32 @@ var AmpSourceAdapter = class {
4633
5054
  }
4634
5055
  async parseFileWithDiagnostics(filePath) {
4635
5056
  const events = [];
4636
- let skippedRows = 0;
4637
5057
  const skippedRowReasons = /* @__PURE__ */ new Map();
4638
- const readResult = await readBoundedJsonFile(filePath);
4639
- if (!readResult.ok) {
4640
- skippedRows++;
4641
- incrementSkippedReason(skippedRowReasons, readResult.reason);
4642
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
4643
- }
4644
- const threadData = readResult.value;
5058
+ const threadData = await readJsonTranscriptFile(filePath);
4645
5059
  const thread = asRecord(threadData);
4646
5060
  if (!thread) {
4647
- skippedRows++;
4648
- incrementSkippedReason(skippedRowReasons, "invalid_thread_data");
4649
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
5061
+ throw new Error(`Amp thread is not a JSON object: ${filePath}`);
4650
5062
  }
4651
5063
  const sessionId = asTrimmedText(thread.id);
4652
5064
  if (!sessionId) {
4653
- skippedRows++;
4654
- incrementSkippedReason(skippedRowReasons, "invalid_thread_id");
4655
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
5065
+ throw new Error(`Amp thread has no id: ${filePath}`);
5066
+ }
5067
+ const ledgerEvents = getLedgerEvents(thread);
5068
+ if (!ledgerEvents && !Array.isArray(thread.messages)) {
5069
+ throw new Error(`Amp thread has neither a usage ledger nor a messages array: ${filePath}`);
4656
5070
  }
4657
5071
  const context = {
4658
5072
  sessionId,
4659
5073
  threadCreatedTimestamp: normalizeTimestampCandidate(thread.created),
4660
5074
  messages: getThreadMessages(thread),
4661
5075
  events,
4662
- skippedRows,
5076
+ skippedRows: 0,
4663
5077
  skippedRowReasons
4664
5078
  };
4665
- const ledgerEvents = getLedgerEvents(thread);
4666
5079
  if (ledgerEvents) {
4667
5080
  parseLedgerEvents(context, ledgerEvents);
4668
- } else if (Array.isArray(thread.messages)) {
4669
- parseMessageUsage(context);
4670
5081
  } else {
4671
- incrementContextSkippedReason(context, "invalid_messages_array");
5082
+ parseMessageUsage(context);
4672
5083
  }
4673
5084
  return toParseDiagnostics(context.events, context.skippedRows, context.skippedRowReasons);
4674
5085
  }
@@ -4977,9 +5388,6 @@ function querySessionCreatedAt(database) {
4977
5388
  var AntigravitySourceAdapter = class {
4978
5389
  id = "antigravity";
4979
5390
  parserVersion = 2;
4980
- capabilities = {
4981
- fixedProviderRoots: ["google"]
4982
- };
4983
5391
  conversationsDir;
4984
5392
  requireDir;
4985
5393
  pathExists;
@@ -5317,6 +5725,9 @@ function parseUsage(usage) {
5317
5725
  const cacheWriteTokens = normalizeNonNegativeInteger(
5318
5726
  toNumberLike(usage.cache_creation_input_tokens)
5319
5727
  );
5728
+ const cacheWrite1hTokens = normalizeNonNegativeInteger(
5729
+ toNumberLike(asRecord(usage.cache_creation)?.ephemeral_1h_input_tokens)
5730
+ );
5320
5731
  const thinkingTokens = normalizeNonNegativeInteger(
5321
5732
  toNumberLike(asRecord(usage.output_tokens_details)?.thinking_tokens)
5322
5733
  );
@@ -5330,6 +5741,7 @@ function parseUsage(usage) {
5330
5741
  reasoningTokens: Math.min(outputTokens, thinkingTokens),
5331
5742
  cacheReadTokens,
5332
5743
  cacheWriteTokens,
5744
+ cacheWrite1hTokens: Math.min(cacheWriteTokens, cacheWrite1hTokens),
5333
5745
  totalTokens
5334
5746
  };
5335
5747
  }
@@ -5352,7 +5764,7 @@ function comparePendingEvents(left, right4) {
5352
5764
  }
5353
5765
  var ClaudeSourceAdapter = class {
5354
5766
  id = "claude";
5355
- parserVersion = 3;
5767
+ parserVersion = 4;
5356
5768
  capabilities = { eventsPrecedeFileMtime: true };
5357
5769
  rootDirs;
5358
5770
  requireDir;
@@ -5651,15 +6063,9 @@ async function parseClineTaskFile(sourceId, filePath) {
5651
6063
  skippedRows: 0,
5652
6064
  skippedRowReasons: /* @__PURE__ */ new Map()
5653
6065
  };
5654
- const readResult = await readBoundedJsonFile(filePath);
5655
- if (!readResult.ok) {
5656
- incrementContextSkippedReason2(context, readResult.reason);
5657
- return toParseDiagnostics(context.events, context.skippedRows, context.skippedRowReasons);
5658
- }
5659
- const parsed = readResult.value;
6066
+ const parsed = await readJsonTranscriptFile(filePath);
5660
6067
  if (!Array.isArray(parsed)) {
5661
- incrementContextSkippedReason2(context, "invalid_messages_data");
5662
- return toParseDiagnostics(context.events, context.skippedRows, context.skippedRowReasons);
6068
+ throw new Error(`Task history is not a JSON array: ${filePath}`);
5663
6069
  }
5664
6070
  for (const entry of parsed) {
5665
6071
  const record = asRecord(entry);
@@ -5721,7 +6127,7 @@ async function discoverUiMessageFiles(rootDir) {
5721
6127
  }
5722
6128
  var ClineFamilyAdapter = class {
5723
6129
  id;
5724
- parserVersion = 2;
6130
+ parserVersion = 3;
5725
6131
  capabilities = { eventsPrecedeFileMtime: true };
5726
6132
  rootDirs;
5727
6133
  requireDir;
@@ -6434,6 +6840,7 @@ function resolveRepoRootFromSessionStart(line) {
6434
6840
  }
6435
6841
  var DroidSourceAdapter = class {
6436
6842
  id = "droid";
6843
+ parserVersion = 2;
6437
6844
  capabilities = { eventsPrecedeFileMtime: true };
6438
6845
  sessionsDir;
6439
6846
  requireDir;
@@ -6471,18 +6878,10 @@ var DroidSourceAdapter = class {
6471
6878
  const events = [];
6472
6879
  let skippedRows = 0;
6473
6880
  const skippedRowReasons = /* @__PURE__ */ new Map();
6474
- const readResult = await readBoundedJsonFile(filePath);
6475
- if (!readResult.ok) {
6476
- skippedRows++;
6477
- incrementSkippedReason(skippedRowReasons, readResult.reason);
6478
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
6479
- }
6480
- const settingsJson = readResult.value;
6881
+ const settingsJson = await readJsonTranscriptFile(filePath);
6481
6882
  const settings = asRecord(settingsJson);
6482
6883
  if (!settings) {
6483
- skippedRows++;
6484
- incrementSkippedReason(skippedRowReasons, "invalid_settings_data");
6485
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
6884
+ throw new Error(`Droid settings are not a JSON object: ${filePath}`);
6486
6885
  }
6487
6886
  const tokenUsage = asRecord(settings.tokenUsage);
6488
6887
  if (!tokenUsage) {
@@ -6758,7 +7157,7 @@ function resolveTimestamp3(line) {
6758
7157
  }
6759
7158
  var DshSourceAdapter = class {
6760
7159
  id = "dsh";
6761
- parserVersion = 3;
7160
+ parserVersion = 4;
6762
7161
  capabilities = { eventsPrecedeFileMtime: true };
6763
7162
  rootDirs;
6764
7163
  requireDir;
@@ -6805,67 +7204,60 @@ var DshSourceAdapter = class {
6805
7204
  incrementSkippedReason(skippedRowReasons, reason);
6806
7205
  }
6807
7206
  });
6808
- try {
6809
- for await (const line of records) {
6810
- if (line.type === "session") {
6811
- state.sessionId = asTrimmedText(line.id) ?? state.sessionId;
6812
- state.repoRoot = asTrimmedText(line.cwd) ?? state.repoRoot;
6813
- continue;
6814
- }
6815
- if (line.type === "session/title-llm-request") {
6816
- const route = resolveRoute(asRecord(line.data));
6817
- state.provider = route.provider ?? state.provider;
6818
- state.model = route.model ?? state.model;
6819
- continue;
6820
- }
6821
- if (line.type !== "assistant/message") {
6822
- continue;
6823
- }
6824
- const data = asRecord(line.data);
6825
- const message = asRecord(data?.message);
6826
- const usage = asRecord(data?.usage);
6827
- if (!usage) {
6828
- continue;
6829
- }
6830
- const extractedUsage = extractUsage2(usage);
6831
- if (!extractedUsage) {
6832
- skippedRows++;
6833
- incrementSkippedReason(skippedRowReasons, "no_token_usage");
6834
- continue;
6835
- }
6836
- const timestamp = resolveTimestamp3(line);
6837
- if (!timestamp || !state.sessionId) {
6838
- skippedRows++;
6839
- incrementSkippedReason(skippedRowReasons, "invalid_timestamp");
6840
- continue;
6841
- }
6842
- const source = asRecord(message?.source);
6843
- const provider = asTrimmedText(source?.provider) ?? state.provider;
6844
- const model = asTrimmedText(source?.model) ?? state.model;
6845
- try {
6846
- events.push(
6847
- createUsageEvent({
6848
- source: this.id,
6849
- sessionId: state.sessionId,
6850
- timestamp,
6851
- repoRoot: state.repoRoot,
6852
- provider,
6853
- model,
6854
- ...extractedUsage,
6855
- totalTokens: resolveUsageTotalTokens(extractedUsage)
6856
- })
6857
- );
6858
- } catch {
6859
- skippedRows++;
6860
- incrementSkippedReason(skippedRowReasons, "event_creation_failed");
6861
- continue;
6862
- }
7207
+ for await (const line of records) {
7208
+ if (line.type === "session") {
7209
+ state.sessionId = asTrimmedText(line.id) ?? state.sessionId;
7210
+ state.repoRoot = asTrimmedText(line.cwd) ?? state.repoRoot;
7211
+ continue;
6863
7212
  }
6864
- } catch (error) {
6865
- if (error instanceof DshSessionLogUnreadableError) {
6866
- return toParseDiagnostics([], 1, /* @__PURE__ */ new Map([["file_parse_failed", 1]]));
7213
+ if (line.type === "session/title-llm-request") {
7214
+ const route = resolveRoute(asRecord(line.data));
7215
+ state.provider = route.provider ?? state.provider;
7216
+ state.model = route.model ?? state.model;
7217
+ continue;
7218
+ }
7219
+ if (line.type !== "assistant/message") {
7220
+ continue;
7221
+ }
7222
+ const data = asRecord(line.data);
7223
+ const message = asRecord(data?.message);
7224
+ const usage = asRecord(data?.usage);
7225
+ if (!usage) {
7226
+ continue;
7227
+ }
7228
+ const extractedUsage = extractUsage2(usage);
7229
+ if (!extractedUsage) {
7230
+ skippedRows++;
7231
+ incrementSkippedReason(skippedRowReasons, "no_token_usage");
7232
+ continue;
7233
+ }
7234
+ const timestamp = resolveTimestamp3(line);
7235
+ if (!timestamp || !state.sessionId) {
7236
+ skippedRows++;
7237
+ incrementSkippedReason(skippedRowReasons, "invalid_timestamp");
7238
+ continue;
7239
+ }
7240
+ const source = asRecord(message?.source);
7241
+ const provider = asTrimmedText(source?.provider) ?? state.provider;
7242
+ const model = asTrimmedText(source?.model) ?? state.model;
7243
+ try {
7244
+ events.push(
7245
+ createUsageEvent({
7246
+ source: this.id,
7247
+ sessionId: state.sessionId,
7248
+ timestamp,
7249
+ repoRoot: state.repoRoot,
7250
+ provider,
7251
+ model,
7252
+ ...extractedUsage,
7253
+ totalTokens: resolveUsageTotalTokens(extractedUsage)
7254
+ })
7255
+ );
7256
+ } catch {
7257
+ skippedRows++;
7258
+ incrementSkippedReason(skippedRowReasons, "event_creation_failed");
7259
+ continue;
6867
7260
  }
6868
- throw error;
6869
7261
  }
6870
7262
  return toParseDiagnostics(events, skippedRows, skippedRowReasons);
6871
7263
  }
@@ -6949,7 +7341,7 @@ async function discoverSessionFiles(geminiDir) {
6949
7341
  const discoveredChatFiles = await discoverFiles(chatsDir, { extension: ".json" });
6950
7342
  allSessionFiles.push(...discoveredChatFiles);
6951
7343
  }
6952
- return allSessionFiles.sort(compareByCodePoint);
7344
+ return [...new Set(allSessionFiles)].sort(compareByCodePoint);
6953
7345
  }
6954
7346
  function resolveRepoRoot(filePath, sessionData, projectMapping) {
6955
7347
  const projectHash = asTrimmedText(sessionData.projectHash);
@@ -7011,7 +7403,7 @@ function extractTokenUsage(tokens) {
7011
7403
  }
7012
7404
  var GeminiSourceAdapter = class {
7013
7405
  id = "gemini";
7014
- parserVersion = 3;
7406
+ parserVersion = 4;
7015
7407
  capabilities = {
7016
7408
  fixedProviderRoots: ["google"],
7017
7409
  eventsPrecedeFileMtime: true
@@ -7063,26 +7455,16 @@ var GeminiSourceAdapter = class {
7063
7455
  const events = [];
7064
7456
  let skippedRows = 0;
7065
7457
  const skippedRowReasons = /* @__PURE__ */ new Map();
7066
- const readResult = await readBoundedJsonFile(filePath);
7067
- if (!readResult.ok) {
7068
- skippedRows++;
7069
- incrementSkippedReason(skippedRowReasons, readResult.reason);
7070
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
7071
- }
7072
- const sessionData = readResult.value;
7458
+ const sessionData = await readJsonTranscriptFile(filePath);
7073
7459
  const sessionDataRecord = asRecord(sessionData);
7074
7460
  if (!sessionDataRecord) {
7075
- skippedRows++;
7076
- incrementSkippedReason(skippedRowReasons, "invalid_session_data");
7077
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
7461
+ throw new Error(`Gemini session is not a JSON object: ${filePath}`);
7078
7462
  }
7079
7463
  const sessionId = asTrimmedText(sessionDataRecord.sessionId) ?? path22.basename(filePath, ".json");
7080
7464
  const projectMapping = await this.getProjectMappingForParse();
7081
7465
  const repoRoot = resolveRepoRoot(filePath, sessionDataRecord, projectMapping);
7082
7466
  if (!Array.isArray(sessionDataRecord.messages)) {
7083
- skippedRows++;
7084
- incrementSkippedReason(skippedRowReasons, "invalid_messages_array");
7085
- return toParseDiagnostics(events, skippedRows, skippedRowReasons);
7467
+ throw new Error(`Gemini session has no messages array: ${filePath}`);
7086
7468
  }
7087
7469
  const messages = sessionDataRecord.messages;
7088
7470
  for (const rawMessage of messages) {
@@ -8433,13 +8815,23 @@ async function readParentSessionPath(filePath) {
8433
8815
  return void 0;
8434
8816
  }
8435
8817
  }
8436
- async function resolveForkedAtMs(sessionLine, filePath, rootDirs) {
8818
+ function getSessionIdFromPath(filePath) {
8819
+ const baseName = path29.basename(filePath, ".jsonl");
8820
+ const separator = baseName.lastIndexOf("_");
8821
+ const sessionId = separator === -1 ? "" : baseName.slice(separator + 1).trim();
8822
+ return sessionId || void 0;
8823
+ }
8824
+ async function resolveFork(sessionLine, filePath, rootDirs) {
8437
8825
  const parentSessionPath = resolveParentSessionPath(sessionLine, filePath);
8438
- if (!parentSessionPath || !isPathWithinRoots(parentSessionPath, rootDirs) || !await pathExists(parentSessionPath)) {
8826
+ const timestamp = normalizeTimestampCandidate(sessionLine.timestamp);
8827
+ if (!parentSessionPath || !timestamp) {
8439
8828
  return void 0;
8440
8829
  }
8441
- const timestamp = normalizeTimestampCandidate(sessionLine.timestamp);
8442
- return timestamp ? Date.parse(timestamp) : void 0;
8830
+ return {
8831
+ forkedAtMs: Date.parse(timestamp),
8832
+ parentCounted: isPathWithinRoots(parentSessionPath, rootDirs) && await pathExists(parentSessionPath),
8833
+ parentSessionId: getSessionIdFromPath(parentSessionPath)
8834
+ };
8443
8835
  }
8444
8836
  function extractUsageFromRecord2(usage) {
8445
8837
  const cost = asRecord(usage.cost);
@@ -8490,8 +8882,12 @@ function resolveRepoRootFromRecord2(record) {
8490
8882
  }
8491
8883
  var PiSourceAdapter = class {
8492
8884
  id = "pi";
8493
- parserVersion = 4;
8494
- capabilities = { eventsPrecedeFileMtime: true };
8885
+ parserVersion = 5;
8886
+ // Two forks of a deleted parent both keep its copied entries under the parent's id.
8887
+ capabilities = {
8888
+ eventsPrecedeFileMtime: true,
8889
+ eventsRepeatAcrossFiles: true
8890
+ };
8495
8891
  rootDirs;
8496
8892
  requireDir;
8497
8893
  constructor(options = {}) {
@@ -8533,7 +8929,7 @@ var PiSourceAdapter = class {
8533
8929
  state.sessionId = asTrimmedText(line.id) ?? state.sessionId;
8534
8930
  state.sessionTimestamp = asTrimmedText(line.timestamp) ?? state.sessionTimestamp;
8535
8931
  state.repoRoot = resolveRepoRootFromRecord2(line) ?? state.repoRoot;
8536
- state.forkedAtMs = await resolveForkedAtMs(line, filePath, this.rootDirs) ?? state.forkedAtMs;
8932
+ state.fork = await resolveFork(line, filePath, this.rootDirs) ?? state.fork;
8537
8933
  continue;
8538
8934
  }
8539
8935
  if (line.type === "model_change") {
@@ -8562,7 +8958,8 @@ var PiSourceAdapter = class {
8562
8958
  incrementSkippedReason(skippedRowReasons, "invalid_timestamp");
8563
8959
  continue;
8564
8960
  }
8565
- if (state.forkedAtMs !== void 0 && Date.parse(timestamp) < state.forkedAtMs) {
8961
+ const copiedFrom = state.fork && Date.parse(timestamp) < state.fork.forkedAtMs ? state.fork : void 0;
8962
+ if (copiedFrom?.parentCounted) {
8566
8963
  continue;
8567
8964
  }
8568
8965
  const model = asTrimmedText(line.model) ?? asTrimmedText(line.modelId) ?? asTrimmedText(message?.model) ?? state.model;
@@ -8571,7 +8968,7 @@ var PiSourceAdapter = class {
8571
8968
  events.push(
8572
8969
  createUsageEvent({
8573
8970
  source: this.id,
8574
- sessionId: state.sessionId,
8971
+ sessionId: copiedFrom?.parentSessionId ?? state.sessionId,
8575
8972
  timestamp,
8576
8973
  repoRoot,
8577
8974
  provider,
@@ -9386,331 +9783,6 @@ function formatEnvVarOverrides(overrides) {
9386
9783
 
9387
9784
  // src/cli/history-live-files.ts
9388
9785
  import { stat as stat4 } from "fs/promises";
9389
-
9390
- // src/persistence/event-store-history.ts
9391
- function toPositiveCount(value) {
9392
- return toNonNegativeInteger(value) ?? 0;
9393
- }
9394
- function normalizeHistorySource(source) {
9395
- return normalizeSourceId(source)?.toLowerCase();
9396
- }
9397
- function normalizeHistoryFilePath(filePath) {
9398
- const normalized = filePath.trim();
9399
- return normalized || void 0;
9400
- }
9401
- function createTempTables(store) {
9402
- store.database.exec(`
9403
- CREATE TEMP TABLE IF NOT EXISTS history_selected_sources (
9404
- source TEXT PRIMARY KEY
9405
- );
9406
- CREATE TEMP TABLE IF NOT EXISTS history_discovered_files (
9407
- source TEXT NOT NULL,
9408
- file_path TEXT NOT NULL,
9409
- counted INTEGER NOT NULL,
9410
- PRIMARY KEY (source, file_path)
9411
- );
9412
- CREATE TEMP TABLE IF NOT EXISTS history_departed_files (
9413
- source TEXT NOT NULL,
9414
- file_path TEXT NOT NULL,
9415
- PRIMARY KEY (source, file_path)
9416
- );
9417
- CREATE TEMP TABLE IF NOT EXISTS history_served_files (
9418
- source TEXT NOT NULL,
9419
- file_path TEXT NOT NULL,
9420
- ordinal INTEGER NOT NULL,
9421
- PRIMARY KEY (source, file_path)
9422
- );
9423
- DELETE FROM history_selected_sources;
9424
- DELETE FROM history_discovered_files;
9425
- DELETE FROM history_departed_files;
9426
- DELETE FROM history_served_files;
9427
- `);
9428
- }
9429
- function writeTempInputs(store, input) {
9430
- const selectedSources = /* @__PURE__ */ new Set();
9431
- const insertSelectedSource = store.database.prepare(
9432
- "INSERT OR IGNORE INTO history_selected_sources (source) VALUES (?)"
9433
- );
9434
- const insertDiscoveredFile = store.database.prepare(
9435
- [
9436
- "INSERT OR IGNORE INTO history_discovered_files (source, file_path, counted)",
9437
- "VALUES (?, ?, ?)"
9438
- ].join("\n")
9439
- );
9440
- for (const source of input.selectedSources) {
9441
- const normalizedSource = normalizeHistorySource(source);
9442
- if (!normalizedSource) {
9443
- continue;
9444
- }
9445
- selectedSources.add(normalizedSource);
9446
- insertSelectedSource.run(normalizedSource);
9447
- }
9448
- const insertLiveFiles = (files, counted) => {
9449
- for (const file of files) {
9450
- const normalizedSource = normalizeHistorySource(file.source);
9451
- const normalizedFilePath = normalizeHistoryFilePath(file.filePath);
9452
- if (!normalizedSource || !normalizedFilePath || !selectedSources.has(normalizedSource)) {
9453
- continue;
9454
- }
9455
- insertDiscoveredFile.run(normalizedSource, normalizedFilePath, counted ? 1 : 0);
9456
- }
9457
- };
9458
- insertLiveFiles(input.discoveredFiles, true);
9459
- insertLiveFiles(input.presentFiles ?? [], false);
9460
- return selectedSources;
9461
- }
9462
- function historyFileKey(source, filePath) {
9463
- return `${source}\0${filePath}`;
9464
- }
9465
- function readUndiscoveredStoredFiles(store, input) {
9466
- const selectedSources = /* @__PURE__ */ new Set();
9467
- for (const source of input.selectedSources) {
9468
- const normalizedSource = normalizeHistorySource(source);
9469
- if (normalizedSource) {
9470
- selectedSources.add(normalizedSource);
9471
- }
9472
- }
9473
- const discoveredKeys = /* @__PURE__ */ new Set();
9474
- for (const discoveredFile of input.discoveredFiles) {
9475
- const normalizedSource = normalizeHistorySource(discoveredFile.source);
9476
- const normalizedFilePath = normalizeHistoryFilePath(discoveredFile.filePath);
9477
- if (normalizedSource && normalizedFilePath) {
9478
- discoveredKeys.add(historyFileKey(normalizedSource, normalizedFilePath));
9479
- }
9480
- }
9481
- const rows = store.database.prepare("SELECT source, file_path FROM files ORDER BY source ASC, file_path ASC").all();
9482
- const undiscoveredFiles = [];
9483
- for (const row of rows) {
9484
- const source = toText(row.source);
9485
- const filePath = toText(row.file_path);
9486
- if (source && filePath && selectedSources.has(source) && !discoveredKeys.has(historyFileKey(source, filePath))) {
9487
- undiscoveredFiles.push({ source, filePath });
9488
- }
9489
- }
9490
- return undiscoveredFiles;
9491
- }
9492
- function addHashCount(target, hash, count) {
9493
- if (count <= 0) {
9494
- return;
9495
- }
9496
- target.set(hash, (target.get(hash) ?? 0) + count);
9497
- }
9498
- function readLiveHashCounts(store) {
9499
- const rows = store.database.prepare(
9500
- [
9501
- "SELECT events.content_hash AS content_hash, COUNT(*) AS count",
9502
- "FROM history_discovered_files AS discovered",
9503
- "JOIN history_selected_sources AS selected",
9504
- " ON discovered.source = selected.source",
9505
- "CROSS JOIN events",
9506
- "WHERE discovered.counted = 1",
9507
- " AND events.source = discovered.source",
9508
- " AND events.file_path = discovered.file_path",
9509
- " AND events.content_hash IS NOT NULL",
9510
- "GROUP BY events.content_hash"
9511
- ].join("\n")
9512
- ).all();
9513
- const counts = /* @__PURE__ */ new Map();
9514
- for (const row of rows) {
9515
- const hash = toText(row.content_hash);
9516
- if (!hash) {
9517
- continue;
9518
- }
9519
- addHashCount(counts, hash, toPositiveCount(row.count));
9520
- }
9521
- return counts;
9522
- }
9523
- function compareDepartedFiles(left, right4) {
9524
- if (left.ingestedAt !== right4.ingestedAt) {
9525
- return left.ingestedAt - right4.ingestedAt;
9526
- }
9527
- if (left.filePath !== right4.filePath) {
9528
- return compareByCodePoint(left.filePath, right4.filePath);
9529
- }
9530
- return compareByCodePoint(left.source, right4.source);
9531
- }
9532
- function readDepartedFiles(store) {
9533
- const rows = store.database.prepare(
9534
- [
9535
- "SELECT files.source, files.file_path, files.ingested_at,",
9536
- " COUNT(events.id) AS event_count, MAX(events.timestamp) AS newest_timestamp",
9537
- "FROM files",
9538
- "JOIN history_selected_sources AS selected",
9539
- " ON files.source = selected.source",
9540
- "LEFT JOIN history_discovered_files AS discovered",
9541
- " ON files.source = discovered.source",
9542
- " AND files.file_path = discovered.file_path",
9543
- "LEFT JOIN events",
9544
- " ON files.source = events.source",
9545
- " AND files.file_path = events.file_path",
9546
- "WHERE discovered.file_path IS NULL",
9547
- "GROUP BY files.source, files.file_path, files.ingested_at"
9548
- ].join("\n")
9549
- ).all();
9550
- const departedFiles = [];
9551
- for (const row of rows) {
9552
- const source = toText(row.source);
9553
- const filePath = toText(row.file_path);
9554
- const ingestedAt = toNonNegativeInteger(row.ingested_at);
9555
- const eventCount = toNonNegativeInteger(row.event_count);
9556
- const newestTimestamp = toText(row.newest_timestamp);
9557
- if (!source || !filePath || ingestedAt === void 0 || eventCount === void 0) {
9558
- continue;
9559
- }
9560
- departedFiles.push({ source, filePath, ingestedAt, eventCount, newestTimestamp });
9561
- }
9562
- return departedFiles.sort(compareDepartedFiles);
9563
- }
9564
- function fileKey(source, filePath) {
9565
- return JSON.stringify([source, filePath]);
9566
- }
9567
- function readDepartedHashMultisets(store) {
9568
- store.database.prepare(
9569
- [
9570
- "INSERT INTO history_departed_files (source, file_path)",
9571
- "SELECT files.source, files.file_path",
9572
- "FROM files",
9573
- "JOIN history_selected_sources AS selected",
9574
- " ON files.source = selected.source",
9575
- "LEFT JOIN history_discovered_files AS discovered",
9576
- " ON files.source = discovered.source",
9577
- " AND files.file_path = discovered.file_path",
9578
- "WHERE discovered.file_path IS NULL"
9579
- ].join("\n")
9580
- ).run();
9581
- const rows = store.database.prepare(
9582
- [
9583
- "SELECT events.source AS source, events.file_path AS file_path,",
9584
- " events.content_hash AS content_hash, COUNT(*) AS count",
9585
- "FROM history_departed_files AS departed",
9586
- "CROSS JOIN events",
9587
- "WHERE events.source = departed.source",
9588
- " AND events.file_path = departed.file_path",
9589
- "GROUP BY events.source, events.file_path, events.content_hash"
9590
- ].join("\n")
9591
- ).all();
9592
- const multisets = /* @__PURE__ */ new Map();
9593
- for (const row of rows) {
9594
- const source = toText(row.source);
9595
- const filePath = toText(row.file_path);
9596
- if (!source || !filePath) {
9597
- continue;
9598
- }
9599
- const key = fileKey(source, filePath);
9600
- let multiset = multisets.get(key);
9601
- if (!multiset) {
9602
- multiset = { counts: /* @__PURE__ */ new Map(), hasNullHash: false };
9603
- multisets.set(key, multiset);
9604
- }
9605
- const hash = toText(row.content_hash);
9606
- if (!hash) {
9607
- multiset.hasNullHash = true;
9608
- continue;
9609
- }
9610
- addHashCount(multiset.counts, hash, toPositiveCount(row.count));
9611
- }
9612
- return multisets;
9613
- }
9614
- function isSubsetOfServedData(fileHashCounts, servedHashCounts) {
9615
- if (fileHashCounts.hasNullHash) {
9616
- return false;
9617
- }
9618
- for (const [hash, count] of fileHashCounts.counts.entries()) {
9619
- if (count > (servedHashCounts.get(hash) ?? 0)) {
9620
- return false;
9621
- }
9622
- }
9623
- return true;
9624
- }
9625
- function addFileHashCounts(servedHashCounts, fileHashCounts) {
9626
- for (const [hash, count] of fileHashCounts.counts.entries()) {
9627
- addHashCount(servedHashCounts, hash, count);
9628
- }
9629
- }
9630
- function loadServedEvents(store, servedFiles) {
9631
- if (servedFiles.length === 0) {
9632
- return [];
9633
- }
9634
- const INSERT_CHUNK_FILES = 500;
9635
- for (let start = 0; start < servedFiles.length; start += INSERT_CHUNK_FILES) {
9636
- const chunk = servedFiles.slice(start, start + INSERT_CHUNK_FILES);
9637
- const placeholders = chunk.map(() => "(?, ?, ?)").join(", ");
9638
- const params = [];
9639
- chunk.forEach((file, index) => {
9640
- params.push(file.source, file.filePath, start + index);
9641
- });
9642
- store.database.prepare(
9643
- `INSERT INTO history_served_files (source, file_path, ordinal) VALUES ${placeholders}`
9644
- ).run(...params);
9645
- }
9646
- const rows = store.database.prepare(
9647
- [
9648
- "SELECT events.source, events.session_id, events.timestamp, events.model,",
9649
- " events.provider, events.repo_root, events.input_tokens, events.output_tokens,",
9650
- " events.reasoning_tokens, events.cache_read_tokens, events.cache_write_tokens,",
9651
- " events.total_tokens, events.cost_usd, events.cost_mode",
9652
- "FROM history_served_files AS served",
9653
- "CROSS JOIN events",
9654
- "WHERE events.source = served.source",
9655
- " AND events.file_path = served.file_path",
9656
- "ORDER BY served.ordinal ASC, events.event_index ASC"
9657
- ].join("\n")
9658
- ).all();
9659
- const events = [];
9660
- for (const row of rows) {
9661
- const event = normalizeStoredEvent(row);
9662
- if (!event) {
9663
- continue;
9664
- }
9665
- events.push(event);
9666
- }
9667
- return events;
9668
- }
9669
- function classifyDepartedFiles(store, input) {
9670
- createTempTables(store);
9671
- const selectedSources = writeTempInputs(store, input);
9672
- if (selectedSources.size === 0) {
9673
- return [];
9674
- }
9675
- const servedHashCounts = readLiveHashCounts(store);
9676
- const departedFiles = readDepartedFiles(store);
9677
- const departedHashMultisets = readDepartedHashMultisets(store);
9678
- const classifiedFiles = [];
9679
- for (const departedFile of departedFiles) {
9680
- const fileHashCounts = departedHashMultisets.get(
9681
- fileKey(departedFile.source, departedFile.filePath)
9682
- ) ?? { counts: /* @__PURE__ */ new Map(), hasNullHash: false };
9683
- const suppressed = isSubsetOfServedData(fileHashCounts, servedHashCounts);
9684
- classifiedFiles.push({
9685
- source: departedFile.source,
9686
- filePath: departedFile.filePath,
9687
- eventCount: departedFile.eventCount,
9688
- newestTimestamp: departedFile.newestTimestamp,
9689
- suppressed
9690
- });
9691
- if (suppressed) {
9692
- continue;
9693
- }
9694
- addFileHashCounts(servedHashCounts, fileHashCounts);
9695
- }
9696
- return classifiedFiles;
9697
- }
9698
- function loadHistoryEvents(store, input) {
9699
- const departedFiles = classifyDepartedFiles(store, input);
9700
- const servedFiles = departedFiles.filter((file) => !file.suppressed);
9701
- const events = loadServedEvents(store, servedFiles);
9702
- const suppressedFileCount = departedFiles.length - servedFiles.length;
9703
- return {
9704
- events,
9705
- servedFiles: servedFiles.map(({ source, filePath }) => ({ source, filePath })),
9706
- departedFileCount: departedFiles.length,
9707
- servedFileCount: servedFiles.length,
9708
- suppressedFileCount,
9709
- servedEventCount: events.length
9710
- };
9711
- }
9712
-
9713
- // src/cli/history-live-files.ts
9714
9786
  var MAX_CONCURRENT_STATS = 32;
9715
9787
  async function checkDiskPresence(filePath, statFile) {
9716
9788
  try {
@@ -10183,6 +10255,24 @@ async function runParseWorker() {
10183
10255
 
10184
10256
  // src/cli/build-usage-data-parsing.ts
10185
10257
  var STORE_WRITE_BATCH_EVENTS = 5e3;
10258
+ function countRepeatedEventsOnce(eventsByFile) {
10259
+ const fileIndexBySession = /* @__PURE__ */ new Map();
10260
+ const sharedSessions = /* @__PURE__ */ new Set();
10261
+ eventsByFile.forEach((events, fileIndex) => {
10262
+ for (const event of events) {
10263
+ const ownerIndex = fileIndexBySession.get(event.sessionId);
10264
+ if (ownerIndex === void 0) {
10265
+ fileIndexBySession.set(event.sessionId, fileIndex);
10266
+ } else if (ownerIndex !== fileIndex) {
10267
+ sharedSessions.add(event.sessionId);
10268
+ }
10269
+ }
10270
+ });
10271
+ const countedHashes = /* @__PURE__ */ new Map();
10272
+ return eventsByFile.flatMap(
10273
+ (events) => events.some((event) => sharedSessions.has(event.sessionId)) ? takeUncountedEvents(events, countedHashes) : events
10274
+ );
10275
+ }
10186
10276
  function getDefaultParseFileDiagnostics2(events) {
10187
10277
  return { events, skippedRows: 0, skippedRowReasons: [] };
10188
10278
  }
@@ -10213,6 +10303,7 @@ async function parseAdapterEvents(adapter, maxParallelFileParsing, runWithParseB
10213
10303
  events: [],
10214
10304
  filePaths: [],
10215
10305
  parsedFilePaths: [],
10306
+ failedFilePaths: [],
10216
10307
  filesFound: 0,
10217
10308
  skippedRows: 0,
10218
10309
  skippedRowReasons: []
@@ -10222,13 +10313,13 @@ async function parseAdapterEvents(adapter, maxParallelFileParsing, runWithParseB
10222
10313
  const parsedByFile = Array.from({ length: files.length }, () => []);
10223
10314
  const skippedRowsByFile = Array.from({ length: files.length }, () => 0);
10224
10315
  const parsedFileIndexes = /* @__PURE__ */ new Set();
10316
+ const failedFileIndexes = /* @__PURE__ */ new Set();
10225
10317
  const skippedRowReasons = /* @__PURE__ */ new Map();
10226
10318
  let pendingStoreWrites = [];
10227
10319
  let pendingStoreEventCount = 0;
10228
- let failedFiles = 0;
10229
10320
  let lastErrorMessage = "";
10230
10321
  function recordParseFailure(fileIndex, error) {
10231
- failedFiles += 1;
10322
+ failedFileIndexes.add(fileIndex);
10232
10323
  lastErrorMessage = getErrorReason(error);
10233
10324
  skippedRowsByFile[fileIndex] = 1;
10234
10325
  skippedRowReasons.set(
@@ -10461,16 +10552,17 @@ async function parseAdapterEvents(adapter, maxParallelFileParsing, runWithParseB
10461
10552
  await runTaskLoop(fileIndices, parseFileAtIndexInline);
10462
10553
  }
10463
10554
  flushPendingStoreWrites();
10464
- if (failedFiles === files.length) {
10555
+ if (failedFileIndexes.size === files.length) {
10465
10556
  throw new Error(
10466
10557
  `All ${files.length} file(s) failed to parse for source ${adapter.id}: ${lastErrorMessage}`
10467
10558
  );
10468
10559
  }
10469
10560
  const result = {
10470
10561
  source: adapter.id,
10471
- events: parsedByFile.flat(),
10562
+ events: adapter.capabilities?.eventsRepeatAcrossFiles ? countRepeatedEventsOnce(parsedByFile) : parsedByFile.flat(),
10472
10563
  filePaths: files,
10473
10564
  parsedFilePaths: files.filter((_, fileIndex) => parsedFileIndexes.has(fileIndex)),
10565
+ failedFilePaths: files.filter((_, fileIndex) => failedFileIndexes.has(fileIndex)),
10474
10566
  filesFound: files.length,
10475
10567
  skippedRows: skippedRowsByFile.reduce((sum, skippedRowsCount) => sum + skippedRowsCount, 0),
10476
10568
  skippedRowReasons: [...skippedRowReasons.entries()].map(([reason, count]) => ({ reason, count })).sort((left, right4) => compareByCodePoint(left.reason, right4.reason))
@@ -10543,13 +10635,15 @@ async function parseSelectedAdapters(adaptersToParse, maxParallelFileParsing, op
10543
10635
  const successfulParseResults = [];
10544
10636
  const discoveredFiles = [];
10545
10637
  const parsedFiles = [];
10638
+ const failedFiles = [];
10546
10639
  for (const [index, parseResult] of parseResults.entries()) {
10547
10640
  const source = adaptersToParse[index].id;
10548
10641
  if (parseResult.status === "fulfilled") {
10549
- const { filePaths, parsedFilePaths, ...parseResultWithoutFiles } = parseResult.value;
10642
+ const { filePaths, parsedFilePaths, failedFilePaths, ...parseResultWithoutFiles } = parseResult.value;
10550
10643
  successfulParseResults.push(parseResultWithoutFiles);
10551
10644
  discoveredFiles.push(...filePaths.map((filePath) => ({ source, filePath })));
10552
10645
  parsedFiles.push(...parsedFilePaths.map((filePath) => ({ source, filePath })));
10646
+ failedFiles.push(...failedFilePaths.map((filePath) => ({ source, filePath })));
10553
10647
  continue;
10554
10648
  }
10555
10649
  sourceFailures.push({ source, reason: getErrorReason(parseResult.reason) });
@@ -10558,6 +10652,7 @@ async function parseSelectedAdapters(adaptersToParse, maxParallelFileParsing, op
10558
10652
  successfulParseResults,
10559
10653
  discoveredFiles,
10560
10654
  parsedFiles,
10655
+ failedFiles,
10561
10656
  eventStoreAvailable: Boolean(options.eventStore?.enabled) && !eventStoreFailureState.disabled,
10562
10657
  sourceFailures,
10563
10658
  warnings: eventStoreFailureState.warning ? [eventStoreFailureState.warning] : []
@@ -10776,6 +10871,7 @@ function toUsageEventInput(value) {
10776
10871
  reasoningTokens: optionalCount(record, "reasoningTokens"),
10777
10872
  cacheReadTokens: optionalCount(record, "cacheReadTokens"),
10778
10873
  cacheWriteTokens: optionalCount(record, "cacheWriteTokens"),
10874
+ cacheWrite1hTokens: optionalCount(record, "cacheWrite1hTokens"),
10779
10875
  totalTokens: optionalCount(record, "totalTokens"),
10780
10876
  costUsd: optionalCount(record, "costUsd"),
10781
10877
  costMode
@@ -11613,7 +11709,13 @@ function calculateEstimatedCostUsd(event, pricing) {
11613
11709
  const inputCost = estimateTokenGroupCost(event.inputTokens, pricing.inputPer1MUsd);
11614
11710
  const outputCost = estimateTokenGroupCost(billedOutput.outputTokens, pricing.outputPer1MUsd);
11615
11711
  const cacheReadCost = estimateTokenGroupCost(event.cacheReadTokens, pricing.cacheReadPer1MUsd);
11616
- const cacheWriteCost = estimateTokenGroupCost(event.cacheWriteTokens, pricing.cacheWritePer1MUsd);
11712
+ const cacheWriteCost = estimateTokenGroupCost(
11713
+ event.cacheWriteTokens - event.cacheWrite1hTokens,
11714
+ pricing.cacheWritePer1MUsd
11715
+ ) + estimateTokenGroupCost(
11716
+ event.cacheWrite1hTokens,
11717
+ pricing.cacheWrite1hPer1MUsd ?? pricing.cacheWritePer1MUsd
11718
+ );
11617
11719
  const reasoningCost = estimateTokenGroupCost(
11618
11720
  billedOutput.reasoningTokens,
11619
11721
  pricing.reasoningPer1MUsd
@@ -11702,7 +11804,12 @@ function applyPricingToEvents(events, pricingSource) {
11702
11804
 
11703
11805
  // src/pricing/pricing-override-source.ts
11704
11806
  var requiredRateKeys = ["inputPer1MUsd", "outputPer1MUsd"];
11705
- var optionalRateKeys = ["cacheReadPer1MUsd", "cacheWritePer1MUsd", "reasoningPer1MUsd"];
11807
+ var optionalRateKeys = [
11808
+ "cacheReadPer1MUsd",
11809
+ "cacheWritePer1MUsd",
11810
+ "cacheWrite1hPer1MUsd",
11811
+ "reasoningPer1MUsd"
11812
+ ];
11706
11813
  var knownOverrideKeys = /* @__PURE__ */ new Set([
11707
11814
  ...requiredRateKeys,
11708
11815
  ...optionalRateKeys,
@@ -11749,6 +11856,7 @@ function normalizePricingOverride(raw, problems) {
11749
11856
  const outputPer1MUsd = readRate(raw, "outputPer1MUsd", true, problems);
11750
11857
  const cacheReadPer1MUsd = readRate(raw, "cacheReadPer1MUsd", false, problems);
11751
11858
  const cacheWritePer1MUsd = readRate(raw, "cacheWritePer1MUsd", false, problems);
11859
+ const cacheWrite1hPer1MUsd = readRate(raw, "cacheWrite1hPer1MUsd", false, problems);
11752
11860
  const reasoningPer1MUsd = readRate(raw, "reasoningPer1MUsd", false, problems);
11753
11861
  const reasoningBilling = normalizeReasoningBilling(raw.reasoningBilling);
11754
11862
  if (raw.reasoningBilling !== void 0 && reasoningBilling === void 0) {
@@ -11762,6 +11870,7 @@ function normalizePricingOverride(raw, problems) {
11762
11870
  outputPer1MUsd,
11763
11871
  ...cacheReadPer1MUsd !== void 0 ? { cacheReadPer1MUsd } : {},
11764
11872
  ...cacheWritePer1MUsd !== void 0 ? { cacheWritePer1MUsd } : {},
11873
+ ...cacheWrite1hPer1MUsd !== void 0 ? { cacheWrite1hPer1MUsd } : {},
11765
11874
  ...reasoningPer1MUsd !== void 0 ? { reasoningPer1MUsd } : {},
11766
11875
  ...reasoningBilling !== void 0 ? { reasoningBilling } : {}
11767
11876
  };
@@ -12716,31 +12825,36 @@ var litellm_pricing_snapshot_default = {
12716
12825
  inputPer1MUsd: 10,
12717
12826
  outputPer1MUsd: 50,
12718
12827
  cacheReadPer1MUsd: 1,
12719
- cacheWritePer1MUsd: 12.5
12828
+ cacheWritePer1MUsd: 12.5,
12829
+ cacheWrite1hPer1MUsd: 20
12720
12830
  },
12721
12831
  "anthropic.claude-fable-5-1": {
12722
12832
  inputPer1MUsd: 10,
12723
12833
  outputPer1MUsd: 50,
12724
12834
  cacheReadPer1MUsd: 0.25,
12725
- cacheWritePer1MUsd: 12.5
12835
+ cacheWritePer1MUsd: 12.5,
12836
+ cacheWrite1hPer1MUsd: 20
12726
12837
  },
12727
12838
  "anthropic.claude-haiku-4-5-20251001-v1:0": {
12728
12839
  inputPer1MUsd: 1,
12729
12840
  outputPer1MUsd: 5,
12730
12841
  cacheReadPer1MUsd: 0.09999999999999999,
12731
- cacheWritePer1MUsd: 1.25
12842
+ cacheWritePer1MUsd: 1.25,
12843
+ cacheWrite1hPer1MUsd: 2
12732
12844
  },
12733
12845
  "anthropic.claude-haiku-4-5@20251001": {
12734
12846
  inputPer1MUsd: 1,
12735
12847
  outputPer1MUsd: 5,
12736
12848
  cacheReadPer1MUsd: 0.09999999999999999,
12737
- cacheWritePer1MUsd: 1.25
12849
+ cacheWritePer1MUsd: 1.25,
12850
+ cacheWrite1hPer1MUsd: 2
12738
12851
  },
12739
12852
  "anthropic.claude-haiku-5-5": {
12740
12853
  inputPer1MUsd: 0.09999999999999999,
12741
12854
  outputPer1MUsd: 0.5,
12742
12855
  cacheReadPer1MUsd: 0.01,
12743
- cacheWritePer1MUsd: 0.125
12856
+ cacheWritePer1MUsd: 0.125,
12857
+ cacheWrite1hPer1MUsd: 0.19999999999999998
12744
12858
  },
12745
12859
  "anthropic.claude-instant-v1": {
12746
12860
  inputPer1MUsd: 0.7999999999999999,
@@ -12750,13 +12864,15 @@ var litellm_pricing_snapshot_default = {
12750
12864
  inputPer1MUsd: 10,
12751
12865
  outputPer1MUsd: 50,
12752
12866
  cacheReadPer1MUsd: 0.25,
12753
- cacheWritePer1MUsd: 12.5
12867
+ cacheWritePer1MUsd: 12.5,
12868
+ cacheWrite1hPer1MUsd: 20
12754
12869
  },
12755
12870
  "anthropic.claude-mythos-preview": {
12756
12871
  inputPer1MUsd: 27.5,
12757
12872
  outputPer1MUsd: 137.5,
12758
12873
  cacheReadPer1MUsd: 2.75,
12759
- cacheWritePer1MUsd: 34.375
12874
+ cacheWritePer1MUsd: 34.375,
12875
+ cacheWrite1hPer1MUsd: 55
12760
12876
  },
12761
12877
  "anthropic.claude-opus-4-1-20250805-v1:0": {
12762
12878
  inputPer1MUsd: 15,
@@ -12774,37 +12890,43 @@ var litellm_pricing_snapshot_default = {
12774
12890
  inputPer1MUsd: 5,
12775
12891
  outputPer1MUsd: 25,
12776
12892
  cacheReadPer1MUsd: 0.5,
12777
- cacheWritePer1MUsd: 6.25
12893
+ cacheWritePer1MUsd: 6.25,
12894
+ cacheWrite1hPer1MUsd: 10
12778
12895
  },
12779
12896
  "anthropic.claude-opus-4-6-v1": {
12780
12897
  inputPer1MUsd: 5,
12781
12898
  outputPer1MUsd: 25,
12782
12899
  cacheReadPer1MUsd: 0.5,
12783
- cacheWritePer1MUsd: 6.25
12900
+ cacheWritePer1MUsd: 6.25,
12901
+ cacheWrite1hPer1MUsd: 10
12784
12902
  },
12785
12903
  "anthropic.claude-opus-4-7": {
12786
12904
  inputPer1MUsd: 5,
12787
12905
  outputPer1MUsd: 25,
12788
12906
  cacheReadPer1MUsd: 0.5,
12789
- cacheWritePer1MUsd: 6.25
12907
+ cacheWritePer1MUsd: 6.25,
12908
+ cacheWrite1hPer1MUsd: 10
12790
12909
  },
12791
12910
  "anthropic.claude-opus-4-8": {
12792
12911
  inputPer1MUsd: 5,
12793
12912
  outputPer1MUsd: 25,
12794
12913
  cacheReadPer1MUsd: 0.5,
12795
- cacheWritePer1MUsd: 6.25
12914
+ cacheWritePer1MUsd: 6.25,
12915
+ cacheWrite1hPer1MUsd: 10
12796
12916
  },
12797
12917
  "anthropic.claude-opus-5": {
12798
12918
  inputPer1MUsd: 5,
12799
12919
  outputPer1MUsd: 25,
12800
12920
  cacheReadPer1MUsd: 0.5,
12801
- cacheWritePer1MUsd: 6.25
12921
+ cacheWritePer1MUsd: 6.25,
12922
+ cacheWrite1hPer1MUsd: 10
12802
12923
  },
12803
12924
  "anthropic.claude-opus-5-5": {
12804
12925
  inputPer1MUsd: 4,
12805
12926
  outputPer1MUsd: 20,
12806
12927
  cacheReadPer1MUsd: 0.19999999999999998,
12807
- cacheWritePer1MUsd: 5
12928
+ cacheWritePer1MUsd: 5,
12929
+ cacheWrite1hPer1MUsd: 8
12808
12930
  },
12809
12931
  "anthropic.claude-sonnet-4-20250514-v1:0": {
12810
12932
  inputPer1MUsd: 3,
@@ -12816,25 +12938,29 @@ var litellm_pricing_snapshot_default = {
12816
12938
  inputPer1MUsd: 3,
12817
12939
  outputPer1MUsd: 15,
12818
12940
  cacheReadPer1MUsd: 0.3,
12819
- cacheWritePer1MUsd: 3.75
12941
+ cacheWritePer1MUsd: 3.75,
12942
+ cacheWrite1hPer1MUsd: 6
12820
12943
  },
12821
12944
  "anthropic.claude-sonnet-4-6": {
12822
12945
  inputPer1MUsd: 3,
12823
12946
  outputPer1MUsd: 15,
12824
12947
  cacheReadPer1MUsd: 0.3,
12825
- cacheWritePer1MUsd: 3.75
12948
+ cacheWritePer1MUsd: 3.75,
12949
+ cacheWrite1hPer1MUsd: 6
12826
12950
  },
12827
12951
  "anthropic.claude-sonnet-5": {
12828
12952
  inputPer1MUsd: 2,
12829
12953
  outputPer1MUsd: 10,
12830
12954
  cacheReadPer1MUsd: 0.19999999999999998,
12831
- cacheWritePer1MUsd: 2.5
12955
+ cacheWritePer1MUsd: 2.5,
12956
+ cacheWrite1hPer1MUsd: 4
12832
12957
  },
12833
12958
  "anthropic.claude-sonnet-5-5": {
12834
12959
  inputPer1MUsd: 2,
12835
12960
  outputPer1MUsd: 10,
12836
12961
  cacheReadPer1MUsd: 0.19999999999999998,
12837
- cacheWritePer1MUsd: 2.5
12962
+ cacheWritePer1MUsd: 2.5,
12963
+ cacheWrite1hPer1MUsd: 4
12838
12964
  },
12839
12965
  "anthropic.claude-v1": {
12840
12966
  inputPer1MUsd: 8,
@@ -12921,7 +13047,8 @@ var litellm_pricing_snapshot_default = {
12921
13047
  inputPer1MUsd: 11,
12922
13048
  outputPer1MUsd: 55,
12923
13049
  cacheReadPer1MUsd: 1.1,
12924
- cacheWritePer1MUsd: 13.75
13050
+ cacheWritePer1MUsd: 13.75,
13051
+ cacheWrite1hPer1MUsd: 22
12925
13052
  },
12926
13053
  "apac.anthropic.claude-haiku-4-5-20251001-v1:0": {
12927
13054
  inputPer1MUsd: 1.1,
@@ -12933,37 +13060,43 @@ var litellm_pricing_snapshot_default = {
12933
13060
  inputPer1MUsd: 0.11,
12934
13061
  outputPer1MUsd: 0.55,
12935
13062
  cacheReadPer1MUsd: 0.011,
12936
- cacheWritePer1MUsd: 0.1375
13063
+ cacheWritePer1MUsd: 0.1375,
13064
+ cacheWrite1hPer1MUsd: 0.22
12937
13065
  },
12938
13066
  "apac.anthropic.claude-mythos-preview": {
12939
13067
  inputPer1MUsd: 27.5,
12940
13068
  outputPer1MUsd: 137.5,
12941
13069
  cacheReadPer1MUsd: 2.75,
12942
- cacheWritePer1MUsd: 34.375
13070
+ cacheWritePer1MUsd: 34.375,
13071
+ cacheWrite1hPer1MUsd: 55
12943
13072
  },
12944
13073
  "apac.anthropic.claude-opus-4-7": {
12945
13074
  inputPer1MUsd: 5.5,
12946
13075
  outputPer1MUsd: 27.5,
12947
13076
  cacheReadPer1MUsd: 0.55,
12948
- cacheWritePer1MUsd: 6.875
13077
+ cacheWritePer1MUsd: 6.875,
13078
+ cacheWrite1hPer1MUsd: 11
12949
13079
  },
12950
13080
  "apac.anthropic.claude-opus-4-8": {
12951
13081
  inputPer1MUsd: 5.5,
12952
13082
  outputPer1MUsd: 27.5,
12953
13083
  cacheReadPer1MUsd: 0.55,
12954
- cacheWritePer1MUsd: 6.875
13084
+ cacheWritePer1MUsd: 6.875,
13085
+ cacheWrite1hPer1MUsd: 11
12955
13086
  },
12956
13087
  "apac.anthropic.claude-opus-5": {
12957
13088
  inputPer1MUsd: 5.5,
12958
13089
  outputPer1MUsd: 27.5,
12959
13090
  cacheReadPer1MUsd: 0.55,
12960
- cacheWritePer1MUsd: 6.875
13091
+ cacheWritePer1MUsd: 6.875,
13092
+ cacheWrite1hPer1MUsd: 11
12961
13093
  },
12962
13094
  "apac.anthropic.claude-opus-5-5": {
12963
13095
  inputPer1MUsd: 4.4,
12964
13096
  outputPer1MUsd: 22,
12965
13097
  cacheReadPer1MUsd: 0.22,
12966
- cacheWritePer1MUsd: 5.5
13098
+ cacheWritePer1MUsd: 5.5,
13099
+ cacheWrite1hPer1MUsd: 8.8
12967
13100
  },
12968
13101
  "apac.anthropic.claude-sonnet-4-20250514-v1:0": {
12969
13102
  inputPer1MUsd: 3,
@@ -12975,97 +13108,113 @@ var litellm_pricing_snapshot_default = {
12975
13108
  inputPer1MUsd: 3.3000000000000003,
12976
13109
  outputPer1MUsd: 16.5,
12977
13110
  cacheReadPer1MUsd: 0.33,
12978
- cacheWritePer1MUsd: 4.125
13111
+ cacheWritePer1MUsd: 4.125,
13112
+ cacheWrite1hPer1MUsd: 6.6000000000000005
12979
13113
  },
12980
13114
  "apac.anthropic.claude-sonnet-5": {
12981
13115
  inputPer1MUsd: 2.2,
12982
13116
  outputPer1MUsd: 11,
12983
13117
  cacheReadPer1MUsd: 0.22,
12984
- cacheWritePer1MUsd: 2.75
13118
+ cacheWritePer1MUsd: 2.75,
13119
+ cacheWrite1hPer1MUsd: 4.4
12985
13120
  },
12986
13121
  "apac.anthropic.claude-sonnet-5-5": {
12987
13122
  inputPer1MUsd: 2.2,
12988
13123
  outputPer1MUsd: 11,
12989
13124
  cacheReadPer1MUsd: 0.22,
12990
- cacheWritePer1MUsd: 2.75
13125
+ cacheWritePer1MUsd: 2.75,
13126
+ cacheWrite1hPer1MUsd: 4.4
12991
13127
  },
12992
13128
  "au.anthropic.claude-fable-5": {
12993
13129
  inputPer1MUsd: 11,
12994
13130
  outputPer1MUsd: 55,
12995
13131
  cacheReadPer1MUsd: 1.1,
12996
- cacheWritePer1MUsd: 13.75
13132
+ cacheWritePer1MUsd: 13.75,
13133
+ cacheWrite1hPer1MUsd: 22
12997
13134
  },
12998
13135
  "au.anthropic.claude-haiku-4-5-20251001-v1:0": {
12999
13136
  inputPer1MUsd: 1.1,
13000
13137
  outputPer1MUsd: 5.5,
13001
13138
  cacheReadPer1MUsd: 0.11,
13002
- cacheWritePer1MUsd: 1.375
13139
+ cacheWritePer1MUsd: 1.375,
13140
+ cacheWrite1hPer1MUsd: 2.2
13003
13141
  },
13004
13142
  "au.anthropic.claude-haiku-5-5": {
13005
13143
  inputPer1MUsd: 0.11,
13006
13144
  outputPer1MUsd: 0.55,
13007
13145
  cacheReadPer1MUsd: 0.011,
13008
- cacheWritePer1MUsd: 0.1375
13146
+ cacheWritePer1MUsd: 0.1375,
13147
+ cacheWrite1hPer1MUsd: 0.22
13009
13148
  },
13010
13149
  "au.anthropic.claude-mythos-preview": {
13011
13150
  inputPer1MUsd: 27.5,
13012
13151
  outputPer1MUsd: 137.5,
13013
13152
  cacheReadPer1MUsd: 2.75,
13014
- cacheWritePer1MUsd: 34.375
13153
+ cacheWritePer1MUsd: 34.375,
13154
+ cacheWrite1hPer1MUsd: 55
13015
13155
  },
13016
13156
  "au.anthropic.claude-opus-4-6-v1": {
13017
13157
  inputPer1MUsd: 5.5,
13018
13158
  outputPer1MUsd: 27.5,
13019
13159
  cacheReadPer1MUsd: 0.55,
13020
- cacheWritePer1MUsd: 6.875
13160
+ cacheWritePer1MUsd: 6.875,
13161
+ cacheWrite1hPer1MUsd: 11
13021
13162
  },
13022
13163
  "au.anthropic.claude-opus-4-7": {
13023
13164
  inputPer1MUsd: 5.5,
13024
13165
  outputPer1MUsd: 27.5,
13025
13166
  cacheReadPer1MUsd: 0.55,
13026
- cacheWritePer1MUsd: 6.875
13167
+ cacheWritePer1MUsd: 6.875,
13168
+ cacheWrite1hPer1MUsd: 11
13027
13169
  },
13028
13170
  "au.anthropic.claude-opus-4-8": {
13029
13171
  inputPer1MUsd: 5.5,
13030
13172
  outputPer1MUsd: 27.5,
13031
13173
  cacheReadPer1MUsd: 0.55,
13032
- cacheWritePer1MUsd: 6.875
13174
+ cacheWritePer1MUsd: 6.875,
13175
+ cacheWrite1hPer1MUsd: 11
13033
13176
  },
13034
13177
  "au.anthropic.claude-opus-5": {
13035
13178
  inputPer1MUsd: 5.5,
13036
13179
  outputPer1MUsd: 27.5,
13037
13180
  cacheReadPer1MUsd: 0.55,
13038
- cacheWritePer1MUsd: 6.875
13181
+ cacheWritePer1MUsd: 6.875,
13182
+ cacheWrite1hPer1MUsd: 11
13039
13183
  },
13040
13184
  "au.anthropic.claude-opus-5-5": {
13041
13185
  inputPer1MUsd: 4.4,
13042
13186
  outputPer1MUsd: 22,
13043
13187
  cacheReadPer1MUsd: 0.22,
13044
- cacheWritePer1MUsd: 5.5
13188
+ cacheWritePer1MUsd: 5.5,
13189
+ cacheWrite1hPer1MUsd: 8.8
13045
13190
  },
13046
13191
  "au.anthropic.claude-sonnet-4-5-20250929-v1:0": {
13047
13192
  inputPer1MUsd: 3.3000000000000003,
13048
13193
  outputPer1MUsd: 16.5,
13049
13194
  cacheReadPer1MUsd: 0.33,
13050
- cacheWritePer1MUsd: 4.125
13195
+ cacheWritePer1MUsd: 4.125,
13196
+ cacheWrite1hPer1MUsd: 6.6000000000000005
13051
13197
  },
13052
13198
  "au.anthropic.claude-sonnet-4-6": {
13053
13199
  inputPer1MUsd: 3.3000000000000003,
13054
13200
  outputPer1MUsd: 16.5,
13055
13201
  cacheReadPer1MUsd: 0.33,
13056
- cacheWritePer1MUsd: 4.125
13202
+ cacheWritePer1MUsd: 4.125,
13203
+ cacheWrite1hPer1MUsd: 6.6000000000000005
13057
13204
  },
13058
13205
  "au.anthropic.claude-sonnet-5": {
13059
13206
  inputPer1MUsd: 2.2,
13060
13207
  outputPer1MUsd: 11,
13061
13208
  cacheReadPer1MUsd: 0.22,
13062
- cacheWritePer1MUsd: 2.75
13209
+ cacheWritePer1MUsd: 2.75,
13210
+ cacheWrite1hPer1MUsd: 4.4
13063
13211
  },
13064
13212
  "au.anthropic.claude-sonnet-5-5": {
13065
13213
  inputPer1MUsd: 2.2,
13066
13214
  outputPer1MUsd: 11,
13067
13215
  cacheReadPer1MUsd: 0.22,
13068
- cacheWritePer1MUsd: 2.75
13216
+ cacheWritePer1MUsd: 2.75,
13217
+ cacheWrite1hPer1MUsd: 4.4
13069
13218
  },
13070
13219
  "azure/ada": {
13071
13220
  inputPer1MUsd: 0.09999999999999999,
@@ -14349,85 +14498,99 @@ var litellm_pricing_snapshot_default = {
14349
14498
  inputPer1MUsd: 10,
14350
14499
  outputPer1MUsd: 50,
14351
14500
  cacheReadPer1MUsd: 1,
14352
- cacheWritePer1MUsd: 12.5
14501
+ cacheWritePer1MUsd: 12.5,
14502
+ cacheWrite1hPer1MUsd: 20
14353
14503
  },
14354
14504
  "azure_ai/claude-fable-5-1": {
14355
14505
  inputPer1MUsd: 10,
14356
14506
  outputPer1MUsd: 50,
14357
14507
  cacheReadPer1MUsd: 0.25,
14358
- cacheWritePer1MUsd: 12.5
14508
+ cacheWritePer1MUsd: 12.5,
14509
+ cacheWrite1hPer1MUsd: 20
14359
14510
  },
14360
14511
  "azure_ai/claude-haiku-4-5": {
14361
14512
  inputPer1MUsd: 1,
14362
14513
  outputPer1MUsd: 5,
14363
14514
  cacheReadPer1MUsd: 0.09999999999999999,
14364
- cacheWritePer1MUsd: 1.25
14515
+ cacheWritePer1MUsd: 1.25,
14516
+ cacheWrite1hPer1MUsd: 2
14365
14517
  },
14366
14518
  "azure_ai/claude-haiku-5-5": {
14367
14519
  inputPer1MUsd: 0.09999999999999999,
14368
14520
  outputPer1MUsd: 0.5,
14369
14521
  cacheReadPer1MUsd: 0.01,
14370
- cacheWritePer1MUsd: 0.125
14522
+ cacheWritePer1MUsd: 0.125,
14523
+ cacheWrite1hPer1MUsd: 0.19999999999999998
14371
14524
  },
14372
14525
  "azure_ai/claude-opus-4-5": {
14373
14526
  inputPer1MUsd: 5,
14374
14527
  outputPer1MUsd: 25,
14375
14528
  cacheReadPer1MUsd: 0.5,
14376
- cacheWritePer1MUsd: 6.25
14529
+ cacheWritePer1MUsd: 6.25,
14530
+ cacheWrite1hPer1MUsd: 10
14377
14531
  },
14378
14532
  "azure_ai/claude-opus-4-6": {
14379
14533
  inputPer1MUsd: 5,
14380
14534
  outputPer1MUsd: 25,
14381
14535
  cacheReadPer1MUsd: 0.5,
14382
- cacheWritePer1MUsd: 6.25
14536
+ cacheWritePer1MUsd: 6.25,
14537
+ cacheWrite1hPer1MUsd: 10
14383
14538
  },
14384
14539
  "azure_ai/claude-opus-4-7": {
14385
14540
  inputPer1MUsd: 5,
14386
14541
  outputPer1MUsd: 25,
14387
14542
  cacheReadPer1MUsd: 0.5,
14388
- cacheWritePer1MUsd: 6.25
14543
+ cacheWritePer1MUsd: 6.25,
14544
+ cacheWrite1hPer1MUsd: 10
14389
14545
  },
14390
14546
  "azure_ai/claude-opus-4-8": {
14391
14547
  inputPer1MUsd: 5,
14392
14548
  outputPer1MUsd: 25,
14393
14549
  cacheReadPer1MUsd: 0.5,
14394
- cacheWritePer1MUsd: 6.25
14550
+ cacheWritePer1MUsd: 6.25,
14551
+ cacheWrite1hPer1MUsd: 10
14395
14552
  },
14396
14553
  "azure_ai/claude-opus-5": {
14397
14554
  inputPer1MUsd: 5,
14398
14555
  outputPer1MUsd: 25,
14399
14556
  cacheReadPer1MUsd: 0.5,
14400
- cacheWritePer1MUsd: 6.25
14557
+ cacheWritePer1MUsd: 6.25,
14558
+ cacheWrite1hPer1MUsd: 10
14401
14559
  },
14402
14560
  "azure_ai/claude-opus-5-5": {
14403
14561
  inputPer1MUsd: 4,
14404
14562
  outputPer1MUsd: 20,
14405
14563
  cacheReadPer1MUsd: 0.19999999999999998,
14406
- cacheWritePer1MUsd: 5
14564
+ cacheWritePer1MUsd: 5,
14565
+ cacheWrite1hPer1MUsd: 8
14407
14566
  },
14408
14567
  "azure_ai/claude-sonnet-4-5": {
14409
14568
  inputPer1MUsd: 3,
14410
14569
  outputPer1MUsd: 15,
14411
14570
  cacheReadPer1MUsd: 0.3,
14412
- cacheWritePer1MUsd: 3.75
14571
+ cacheWritePer1MUsd: 3.75,
14572
+ cacheWrite1hPer1MUsd: 6
14413
14573
  },
14414
14574
  "azure_ai/claude-sonnet-4-6": {
14415
14575
  inputPer1MUsd: 3,
14416
14576
  outputPer1MUsd: 15,
14417
14577
  cacheReadPer1MUsd: 0.3,
14418
- cacheWritePer1MUsd: 3.75
14578
+ cacheWritePer1MUsd: 3.75,
14579
+ cacheWrite1hPer1MUsd: 6
14419
14580
  },
14420
14581
  "azure_ai/claude-sonnet-5": {
14421
14582
  inputPer1MUsd: 2,
14422
14583
  outputPer1MUsd: 10,
14423
14584
  cacheReadPer1MUsd: 0.19999999999999998,
14424
- cacheWritePer1MUsd: 2.5
14585
+ cacheWritePer1MUsd: 2.5,
14586
+ cacheWrite1hPer1MUsd: 4
14425
14587
  },
14426
14588
  "azure_ai/claude-sonnet-5-5": {
14427
14589
  inputPer1MUsd: 2,
14428
14590
  outputPer1MUsd: 10,
14429
14591
  cacheReadPer1MUsd: 0.19999999999999998,
14430
- cacheWritePer1MUsd: 2.5
14592
+ cacheWritePer1MUsd: 2.5,
14593
+ cacheWrite1hPer1MUsd: 4
14431
14594
  },
14432
14595
  "azure_ai/codestral-2501": {
14433
14596
  inputPer1MUsd: 0.3,
@@ -15449,61 +15612,71 @@ var litellm_pricing_snapshot_default = {
15449
15612
  inputPer1MUsd: 12,
15450
15613
  outputPer1MUsd: 60,
15451
15614
  cacheReadPer1MUsd: 0.3,
15452
- cacheWritePer1MUsd: 15
15615
+ cacheWritePer1MUsd: 15,
15616
+ cacheWrite1hPer1MUsd: 24
15453
15617
  },
15454
15618
  "bedrock/us-gov-east-1/anthropic.claude-haiku-4-5-20251001-v1:0": {
15455
15619
  inputPer1MUsd: 1.2,
15456
15620
  outputPer1MUsd: 6,
15457
15621
  cacheReadPer1MUsd: 0.12,
15458
- cacheWritePer1MUsd: 1.5
15622
+ cacheWritePer1MUsd: 1.5,
15623
+ cacheWrite1hPer1MUsd: 2.4
15459
15624
  },
15460
15625
  "bedrock/us-gov-east-1/anthropic.claude-haiku-5-5": {
15461
15626
  inputPer1MUsd: 0.12,
15462
15627
  outputPer1MUsd: 0.6,
15463
15628
  cacheReadPer1MUsd: 0.012,
15464
- cacheWritePer1MUsd: 0.15
15629
+ cacheWritePer1MUsd: 0.15,
15630
+ cacheWrite1hPer1MUsd: 0.24
15465
15631
  },
15466
15632
  "bedrock/us-gov-east-1/anthropic.claude-opus-4-8": {
15467
15633
  inputPer1MUsd: 6,
15468
15634
  outputPer1MUsd: 30,
15469
15635
  cacheReadPer1MUsd: 0.6,
15470
- cacheWritePer1MUsd: 7.5
15636
+ cacheWritePer1MUsd: 7.5,
15637
+ cacheWrite1hPer1MUsd: 12
15471
15638
  },
15472
15639
  "bedrock/us-gov-east-1/anthropic.claude-opus-5": {
15473
15640
  inputPer1MUsd: 6,
15474
15641
  outputPer1MUsd: 30,
15475
15642
  cacheReadPer1MUsd: 0.6,
15476
- cacheWritePer1MUsd: 7.5
15643
+ cacheWritePer1MUsd: 7.5,
15644
+ cacheWrite1hPer1MUsd: 12
15477
15645
  },
15478
15646
  "bedrock/us-gov-east-1/anthropic.claude-opus-5-5": {
15479
15647
  inputPer1MUsd: 4.8,
15480
15648
  outputPer1MUsd: 24,
15481
15649
  cacheReadPer1MUsd: 0.24,
15482
- cacheWritePer1MUsd: 6
15650
+ cacheWritePer1MUsd: 6,
15651
+ cacheWrite1hPer1MUsd: 9.6
15483
15652
  },
15484
15653
  "bedrock/us-gov-east-1/anthropic.claude-sonnet-4-5-20250929-v1:0": {
15485
15654
  inputPer1MUsd: 3.5999999999999996,
15486
15655
  outputPer1MUsd: 18,
15487
15656
  cacheReadPer1MUsd: 0.36,
15488
- cacheWritePer1MUsd: 4.5
15657
+ cacheWritePer1MUsd: 4.5,
15658
+ cacheWrite1hPer1MUsd: 7.199999999999999
15489
15659
  },
15490
15660
  "bedrock/us-gov-east-1/anthropic.claude-sonnet-5": {
15491
15661
  inputPer1MUsd: 2.4,
15492
15662
  outputPer1MUsd: 12,
15493
15663
  cacheReadPer1MUsd: 0.24,
15494
- cacheWritePer1MUsd: 3
15664
+ cacheWritePer1MUsd: 3,
15665
+ cacheWrite1hPer1MUsd: 4.8
15495
15666
  },
15496
15667
  "bedrock/us-gov-east-1/anthropic.claude-sonnet-5-5": {
15497
15668
  inputPer1MUsd: 2.4,
15498
15669
  outputPer1MUsd: 12,
15499
15670
  cacheReadPer1MUsd: 0.24,
15500
- cacheWritePer1MUsd: 3
15671
+ cacheWritePer1MUsd: 3,
15672
+ cacheWrite1hPer1MUsd: 4.8
15501
15673
  },
15502
15674
  "bedrock/us-gov-east-1/claude-sonnet-4-5-20250929-v1:0": {
15503
15675
  inputPer1MUsd: 3.5999999999999996,
15504
15676
  outputPer1MUsd: 18,
15505
15677
  cacheReadPer1MUsd: 0.36,
15506
- cacheWritePer1MUsd: 4.5
15678
+ cacheWritePer1MUsd: 4.5,
15679
+ cacheWrite1hPer1MUsd: 7.199999999999999
15507
15680
  },
15508
15681
  "bedrock/us-gov-east-1/meta.llama3-70b-instruct-v1:0": {
15509
15682
  inputPer1MUsd: 2.65,
@@ -15580,61 +15753,71 @@ var litellm_pricing_snapshot_default = {
15580
15753
  inputPer1MUsd: 12,
15581
15754
  outputPer1MUsd: 60,
15582
15755
  cacheReadPer1MUsd: 0.3,
15583
- cacheWritePer1MUsd: 15
15756
+ cacheWritePer1MUsd: 15,
15757
+ cacheWrite1hPer1MUsd: 24
15584
15758
  },
15585
15759
  "bedrock/us-gov-west-1/anthropic.claude-haiku-4-5-20251001-v1:0": {
15586
15760
  inputPer1MUsd: 1.2,
15587
15761
  outputPer1MUsd: 6,
15588
15762
  cacheReadPer1MUsd: 0.12,
15589
- cacheWritePer1MUsd: 1.5
15763
+ cacheWritePer1MUsd: 1.5,
15764
+ cacheWrite1hPer1MUsd: 2.4
15590
15765
  },
15591
15766
  "bedrock/us-gov-west-1/anthropic.claude-haiku-5-5": {
15592
15767
  inputPer1MUsd: 0.12,
15593
15768
  outputPer1MUsd: 0.6,
15594
15769
  cacheReadPer1MUsd: 0.012,
15595
- cacheWritePer1MUsd: 0.15
15770
+ cacheWritePer1MUsd: 0.15,
15771
+ cacheWrite1hPer1MUsd: 0.24
15596
15772
  },
15597
15773
  "bedrock/us-gov-west-1/anthropic.claude-opus-4-8": {
15598
15774
  inputPer1MUsd: 6,
15599
15775
  outputPer1MUsd: 30,
15600
15776
  cacheReadPer1MUsd: 0.6,
15601
- cacheWritePer1MUsd: 7.5
15777
+ cacheWritePer1MUsd: 7.5,
15778
+ cacheWrite1hPer1MUsd: 12
15602
15779
  },
15603
15780
  "bedrock/us-gov-west-1/anthropic.claude-opus-5": {
15604
15781
  inputPer1MUsd: 6,
15605
15782
  outputPer1MUsd: 30,
15606
15783
  cacheReadPer1MUsd: 0.6,
15607
- cacheWritePer1MUsd: 7.5
15784
+ cacheWritePer1MUsd: 7.5,
15785
+ cacheWrite1hPer1MUsd: 12
15608
15786
  },
15609
15787
  "bedrock/us-gov-west-1/anthropic.claude-opus-5-5": {
15610
15788
  inputPer1MUsd: 4.8,
15611
15789
  outputPer1MUsd: 24,
15612
15790
  cacheReadPer1MUsd: 0.24,
15613
- cacheWritePer1MUsd: 6
15791
+ cacheWritePer1MUsd: 6,
15792
+ cacheWrite1hPer1MUsd: 9.6
15614
15793
  },
15615
15794
  "bedrock/us-gov-west-1/anthropic.claude-sonnet-4-5-20250929-v1:0": {
15616
15795
  inputPer1MUsd: 3.5999999999999996,
15617
15796
  outputPer1MUsd: 18,
15618
15797
  cacheReadPer1MUsd: 0.36,
15619
- cacheWritePer1MUsd: 4.5
15798
+ cacheWritePer1MUsd: 4.5,
15799
+ cacheWrite1hPer1MUsd: 7.199999999999999
15620
15800
  },
15621
15801
  "bedrock/us-gov-west-1/anthropic.claude-sonnet-5": {
15622
15802
  inputPer1MUsd: 2.4,
15623
15803
  outputPer1MUsd: 12,
15624
15804
  cacheReadPer1MUsd: 0.24,
15625
- cacheWritePer1MUsd: 3
15805
+ cacheWritePer1MUsd: 3,
15806
+ cacheWrite1hPer1MUsd: 4.8
15626
15807
  },
15627
15808
  "bedrock/us-gov-west-1/anthropic.claude-sonnet-5-5": {
15628
15809
  inputPer1MUsd: 2.4,
15629
15810
  outputPer1MUsd: 12,
15630
15811
  cacheReadPer1MUsd: 0.24,
15631
- cacheWritePer1MUsd: 3
15812
+ cacheWritePer1MUsd: 3,
15813
+ cacheWrite1hPer1MUsd: 4.8
15632
15814
  },
15633
15815
  "bedrock/us-gov-west-1/claude-sonnet-4-5-20250929-v1:0": {
15634
15816
  inputPer1MUsd: 3.5999999999999996,
15635
15817
  outputPer1MUsd: 18,
15636
15818
  cacheReadPer1MUsd: 0.36,
15637
- cacheWritePer1MUsd: 4.5
15819
+ cacheWritePer1MUsd: 4.5,
15820
+ cacheWrite1hPer1MUsd: 7.199999999999999
15638
15821
  },
15639
15822
  "bedrock/us-gov-west-1/meta.llama3-70b-instruct-v1:0": {
15640
15823
  inputPer1MUsd: 2.65,
@@ -15738,25 +15921,29 @@ var litellm_pricing_snapshot_default = {
15738
15921
  inputPer1MUsd: 1,
15739
15922
  outputPer1MUsd: 5,
15740
15923
  cacheReadPer1MUsd: 0.09999999999999999,
15741
- cacheWritePer1MUsd: 1.25
15924
+ cacheWritePer1MUsd: 1.25,
15925
+ cacheWrite1hPer1MUsd: 2
15742
15926
  },
15743
15927
  "bedrock_mantle/anthropic.claude-haiku-5-5": {
15744
15928
  inputPer1MUsd: 0.11,
15745
15929
  outputPer1MUsd: 0.55,
15746
15930
  cacheReadPer1MUsd: 0.011,
15747
- cacheWritePer1MUsd: 0.1375
15931
+ cacheWritePer1MUsd: 0.1375,
15932
+ cacheWrite1hPer1MUsd: 0.22
15748
15933
  },
15749
15934
  "bedrock_mantle/anthropic.claude-opus-5-5": {
15750
15935
  inputPer1MUsd: 4.4,
15751
15936
  outputPer1MUsd: 22,
15752
15937
  cacheReadPer1MUsd: 0.22,
15753
- cacheWritePer1MUsd: 5.5
15938
+ cacheWritePer1MUsd: 5.5,
15939
+ cacheWrite1hPer1MUsd: 8.8
15754
15940
  },
15755
15941
  "bedrock_mantle/anthropic.claude-sonnet-5-5": {
15756
15942
  inputPer1MUsd: 2.2,
15757
15943
  outputPer1MUsd: 11,
15758
15944
  cacheReadPer1MUsd: 0.22,
15759
- cacheWritePer1MUsd: 2.75
15945
+ cacheWritePer1MUsd: 2.75,
15946
+ cacheWrite1hPer1MUsd: 4.4
15760
15947
  },
15761
15948
  "bedrock_mantle/deepseek.v3.1": {
15762
15949
  inputPer1MUsd: 0.58,
@@ -15904,19 +16091,22 @@ var litellm_pricing_snapshot_default = {
15904
16091
  inputPer1MUsd: 0.12,
15905
16092
  outputPer1MUsd: 0.6,
15906
16093
  cacheReadPer1MUsd: 0.012,
15907
- cacheWritePer1MUsd: 0.15
16094
+ cacheWritePer1MUsd: 0.15,
16095
+ cacheWrite1hPer1MUsd: 0.24
15908
16096
  },
15909
16097
  "bedrock_mantle/us-gov-west-1/anthropic.claude-opus-5-5": {
15910
16098
  inputPer1MUsd: 4.8,
15911
16099
  outputPer1MUsd: 24,
15912
16100
  cacheReadPer1MUsd: 0.24,
15913
- cacheWritePer1MUsd: 6
16101
+ cacheWritePer1MUsd: 6,
16102
+ cacheWrite1hPer1MUsd: 9.6
15914
16103
  },
15915
16104
  "bedrock_mantle/us-gov-west-1/anthropic.claude-sonnet-5-5": {
15916
16105
  inputPer1MUsd: 2.4,
15917
16106
  outputPer1MUsd: 12,
15918
16107
  cacheReadPer1MUsd: 0.24,
15919
- cacheWritePer1MUsd: 3
16108
+ cacheWritePer1MUsd: 3,
16109
+ cacheWrite1hPer1MUsd: 4.8
15920
16110
  },
15921
16111
  "bedrock_mantle/us-gov-west-1/google.gemma-4-26b-a4b": {
15922
16112
  inputPer1MUsd: 0.156,
@@ -16037,139 +16227,162 @@ var litellm_pricing_snapshot_default = {
16037
16227
  inputPer1MUsd: 10,
16038
16228
  outputPer1MUsd: 50,
16039
16229
  cacheReadPer1MUsd: 1,
16040
- cacheWritePer1MUsd: 12.5
16230
+ cacheWritePer1MUsd: 12.5,
16231
+ cacheWrite1hPer1MUsd: 20
16041
16232
  },
16042
16233
  "claude-fable-5-1": {
16043
16234
  inputPer1MUsd: 10,
16044
16235
  outputPer1MUsd: 50,
16045
16236
  cacheReadPer1MUsd: 0.25,
16046
- cacheWritePer1MUsd: 12.5
16237
+ cacheWritePer1MUsd: 12.5,
16238
+ cacheWrite1hPer1MUsd: 20
16047
16239
  },
16048
16240
  "claude-haiku-4-5": {
16049
16241
  inputPer1MUsd: 1,
16050
16242
  outputPer1MUsd: 5,
16051
16243
  cacheReadPer1MUsd: 0.09999999999999999,
16052
- cacheWritePer1MUsd: 1.25
16244
+ cacheWritePer1MUsd: 1.25,
16245
+ cacheWrite1hPer1MUsd: 2
16053
16246
  },
16054
16247
  "claude-haiku-4-5-20251001": {
16055
16248
  inputPer1MUsd: 1,
16056
16249
  outputPer1MUsd: 5,
16057
16250
  cacheReadPer1MUsd: 0.09999999999999999,
16058
- cacheWritePer1MUsd: 1.25
16251
+ cacheWritePer1MUsd: 1.25,
16252
+ cacheWrite1hPer1MUsd: 2
16059
16253
  },
16060
16254
  "claude-haiku-5-5": {
16061
16255
  inputPer1MUsd: 0.09999999999999999,
16062
16256
  outputPer1MUsd: 0.5,
16063
16257
  cacheReadPer1MUsd: 0.01,
16064
- cacheWritePer1MUsd: 0.125
16258
+ cacheWritePer1MUsd: 0.125,
16259
+ cacheWrite1hPer1MUsd: 0.19999999999999998
16065
16260
  },
16066
16261
  "claude-mythos-5": {
16067
16262
  inputPer1MUsd: 10,
16068
16263
  outputPer1MUsd: 50,
16069
16264
  cacheReadPer1MUsd: 1,
16070
- cacheWritePer1MUsd: 12.5
16265
+ cacheWritePer1MUsd: 12.5,
16266
+ cacheWrite1hPer1MUsd: 20
16071
16267
  },
16072
16268
  "claude-mythos-5-1": {
16073
16269
  inputPer1MUsd: 10,
16074
16270
  outputPer1MUsd: 50,
16075
16271
  cacheReadPer1MUsd: 0.25,
16076
- cacheWritePer1MUsd: 12.5
16272
+ cacheWritePer1MUsd: 12.5,
16273
+ cacheWrite1hPer1MUsd: 20
16077
16274
  },
16078
16275
  "claude-mythos-preview": {
16079
16276
  inputPer1MUsd: 10,
16080
16277
  outputPer1MUsd: 50,
16081
16278
  cacheReadPer1MUsd: 1,
16082
- cacheWritePer1MUsd: 12.5
16279
+ cacheWritePer1MUsd: 12.5,
16280
+ cacheWrite1hPer1MUsd: 20
16083
16281
  },
16084
16282
  "claude-opus-4-5": {
16085
16283
  inputPer1MUsd: 5,
16086
16284
  outputPer1MUsd: 25,
16087
16285
  cacheReadPer1MUsd: 0.5,
16088
- cacheWritePer1MUsd: 6.25
16286
+ cacheWritePer1MUsd: 6.25,
16287
+ cacheWrite1hPer1MUsd: 10
16089
16288
  },
16090
16289
  "claude-opus-4-5-20251101": {
16091
16290
  inputPer1MUsd: 5,
16092
16291
  outputPer1MUsd: 25,
16093
16292
  cacheReadPer1MUsd: 0.5,
16094
- cacheWritePer1MUsd: 6.25
16293
+ cacheWritePer1MUsd: 6.25,
16294
+ cacheWrite1hPer1MUsd: 10
16095
16295
  },
16096
16296
  "claude-opus-4-6": {
16097
16297
  inputPer1MUsd: 5,
16098
16298
  outputPer1MUsd: 25,
16099
16299
  cacheReadPer1MUsd: 0.5,
16100
- cacheWritePer1MUsd: 6.25
16300
+ cacheWritePer1MUsd: 6.25,
16301
+ cacheWrite1hPer1MUsd: 10
16101
16302
  },
16102
16303
  "claude-opus-4-6-20260205": {
16103
16304
  inputPer1MUsd: 5,
16104
16305
  outputPer1MUsd: 25,
16105
16306
  cacheReadPer1MUsd: 0.5,
16106
- cacheWritePer1MUsd: 6.25
16307
+ cacheWritePer1MUsd: 6.25,
16308
+ cacheWrite1hPer1MUsd: 10
16107
16309
  },
16108
16310
  "claude-opus-4-7": {
16109
16311
  inputPer1MUsd: 5,
16110
16312
  outputPer1MUsd: 25,
16111
16313
  cacheReadPer1MUsd: 0.5,
16112
- cacheWritePer1MUsd: 6.25
16314
+ cacheWritePer1MUsd: 6.25,
16315
+ cacheWrite1hPer1MUsd: 10
16113
16316
  },
16114
16317
  "claude-opus-4-7-20260416": {
16115
16318
  inputPer1MUsd: 5,
16116
16319
  outputPer1MUsd: 25,
16117
16320
  cacheReadPer1MUsd: 0.5,
16118
- cacheWritePer1MUsd: 6.25
16321
+ cacheWritePer1MUsd: 6.25,
16322
+ cacheWrite1hPer1MUsd: 10
16119
16323
  },
16120
16324
  "claude-opus-4-8": {
16121
16325
  inputPer1MUsd: 5,
16122
16326
  outputPer1MUsd: 25,
16123
16327
  cacheReadPer1MUsd: 0.5,
16124
- cacheWritePer1MUsd: 6.25
16328
+ cacheWritePer1MUsd: 6.25,
16329
+ cacheWrite1hPer1MUsd: 10
16125
16330
  },
16126
16331
  "claude-opus-5": {
16127
16332
  inputPer1MUsd: 5,
16128
16333
  outputPer1MUsd: 25,
16129
16334
  cacheReadPer1MUsd: 0.5,
16130
- cacheWritePer1MUsd: 6.25
16335
+ cacheWritePer1MUsd: 6.25,
16336
+ cacheWrite1hPer1MUsd: 10
16131
16337
  },
16132
16338
  "claude-opus-5-5": {
16133
16339
  inputPer1MUsd: 4,
16134
16340
  outputPer1MUsd: 20,
16135
16341
  cacheReadPer1MUsd: 0.19999999999999998,
16136
- cacheWritePer1MUsd: 5
16342
+ cacheWritePer1MUsd: 5,
16343
+ cacheWrite1hPer1MUsd: 8
16137
16344
  },
16138
16345
  "claude-sonnet-4-5": {
16139
16346
  inputPer1MUsd: 3,
16140
16347
  outputPer1MUsd: 15,
16141
16348
  cacheReadPer1MUsd: 0.3,
16142
- cacheWritePer1MUsd: 3.75
16349
+ cacheWritePer1MUsd: 3.75,
16350
+ cacheWrite1hPer1MUsd: 6
16143
16351
  },
16144
16352
  "claude-sonnet-4-5-20250929": {
16145
16353
  inputPer1MUsd: 3,
16146
16354
  outputPer1MUsd: 15,
16147
16355
  cacheReadPer1MUsd: 0.3,
16148
- cacheWritePer1MUsd: 3.75
16356
+ cacheWritePer1MUsd: 3.75,
16357
+ cacheWrite1hPer1MUsd: 6
16149
16358
  },
16150
16359
  "claude-sonnet-4-5-20250929-v1:0": {
16151
16360
  inputPer1MUsd: 3,
16152
16361
  outputPer1MUsd: 15,
16153
16362
  cacheReadPer1MUsd: 0.3,
16154
- cacheWritePer1MUsd: 3.75
16363
+ cacheWritePer1MUsd: 3.75,
16364
+ cacheWrite1hPer1MUsd: 6
16155
16365
  },
16156
16366
  "claude-sonnet-4-6": {
16157
16367
  inputPer1MUsd: 3,
16158
16368
  outputPer1MUsd: 15,
16159
16369
  cacheReadPer1MUsd: 0.3,
16160
- cacheWritePer1MUsd: 3.75
16370
+ cacheWritePer1MUsd: 3.75,
16371
+ cacheWrite1hPer1MUsd: 6
16161
16372
  },
16162
16373
  "claude-sonnet-5": {
16163
16374
  inputPer1MUsd: 2,
16164
16375
  outputPer1MUsd: 10,
16165
16376
  cacheReadPer1MUsd: 0.19999999999999998,
16166
- cacheWritePer1MUsd: 2.5
16377
+ cacheWritePer1MUsd: 2.5,
16378
+ cacheWrite1hPer1MUsd: 4
16167
16379
  },
16168
16380
  "claude-sonnet-5-5": {
16169
16381
  inputPer1MUsd: 2,
16170
16382
  outputPer1MUsd: 10,
16171
16383
  cacheReadPer1MUsd: 0.09999999999999999,
16172
- cacheWritePer1MUsd: 2.5
16384
+ cacheWritePer1MUsd: 2.5,
16385
+ cacheWrite1hPer1MUsd: 4
16173
16386
  },
16174
16387
  "cloudflare/@cf/aisingapore/gemma-sea-lion-v4-27b-it": {
16175
16388
  inputPer1MUsd: 0.351,
@@ -16632,7 +16845,8 @@ var litellm_pricing_snapshot_default = {
16632
16845
  inputPer1MUsd: 4.00001,
16633
16846
  outputPer1MUsd: 19.99998,
16634
16847
  cacheReadPer1MUsd: 0.19999,
16635
- cacheWritePer1MUsd: 5.00003
16848
+ cacheWritePer1MUsd: 5.00003,
16849
+ cacheWrite1hPer1MUsd: 8.00002
16636
16850
  },
16637
16851
  "databricks/databricks-claude-sonnet-4": {
16638
16852
  inputPer1MUsd: 2.9999900000000004,
@@ -17722,25 +17936,29 @@ var litellm_pricing_snapshot_default = {
17722
17936
  inputPer1MUsd: 11,
17723
17937
  outputPer1MUsd: 55,
17724
17938
  cacheReadPer1MUsd: 1.1,
17725
- cacheWritePer1MUsd: 13.75
17939
+ cacheWritePer1MUsd: 13.75,
17940
+ cacheWrite1hPer1MUsd: 22
17726
17941
  },
17727
17942
  "eu.anthropic.claude-fable-5-1": {
17728
17943
  inputPer1MUsd: 11,
17729
17944
  outputPer1MUsd: 55,
17730
17945
  cacheReadPer1MUsd: 0.275,
17731
- cacheWritePer1MUsd: 13.75
17946
+ cacheWritePer1MUsd: 13.75,
17947
+ cacheWrite1hPer1MUsd: 22
17732
17948
  },
17733
17949
  "eu.anthropic.claude-haiku-4-5-20251001-v1:0": {
17734
17950
  inputPer1MUsd: 1.1,
17735
17951
  outputPer1MUsd: 5.5,
17736
17952
  cacheReadPer1MUsd: 0.11,
17737
- cacheWritePer1MUsd: 1.375
17953
+ cacheWritePer1MUsd: 1.375,
17954
+ cacheWrite1hPer1MUsd: 2.2
17738
17955
  },
17739
17956
  "eu.anthropic.claude-haiku-5-5": {
17740
17957
  inputPer1MUsd: 0.11,
17741
17958
  outputPer1MUsd: 0.55,
17742
17959
  cacheReadPer1MUsd: 0.011,
17743
- cacheWritePer1MUsd: 0.1375
17960
+ cacheWritePer1MUsd: 0.1375,
17961
+ cacheWrite1hPer1MUsd: 0.22
17744
17962
  },
17745
17963
  "eu.anthropic.claude-opus-4-1-20250805-v1:0": {
17746
17964
  inputPer1MUsd: 15,
@@ -17758,37 +17976,43 @@ var litellm_pricing_snapshot_default = {
17758
17976
  inputPer1MUsd: 5.5,
17759
17977
  outputPer1MUsd: 27.5,
17760
17978
  cacheReadPer1MUsd: 0.55,
17761
- cacheWritePer1MUsd: 6.875
17979
+ cacheWritePer1MUsd: 6.875,
17980
+ cacheWrite1hPer1MUsd: 11
17762
17981
  },
17763
17982
  "eu.anthropic.claude-opus-4-6-v1": {
17764
17983
  inputPer1MUsd: 5.5,
17765
17984
  outputPer1MUsd: 27.5,
17766
17985
  cacheReadPer1MUsd: 0.55,
17767
- cacheWritePer1MUsd: 6.875
17986
+ cacheWritePer1MUsd: 6.875,
17987
+ cacheWrite1hPer1MUsd: 11
17768
17988
  },
17769
17989
  "eu.anthropic.claude-opus-4-7": {
17770
17990
  inputPer1MUsd: 5.5,
17771
17991
  outputPer1MUsd: 27.5,
17772
17992
  cacheReadPer1MUsd: 0.55,
17773
- cacheWritePer1MUsd: 6.875
17993
+ cacheWritePer1MUsd: 6.875,
17994
+ cacheWrite1hPer1MUsd: 11
17774
17995
  },
17775
17996
  "eu.anthropic.claude-opus-4-8": {
17776
17997
  inputPer1MUsd: 5.5,
17777
17998
  outputPer1MUsd: 27.5,
17778
17999
  cacheReadPer1MUsd: 0.55,
17779
- cacheWritePer1MUsd: 6.875
18000
+ cacheWritePer1MUsd: 6.875,
18001
+ cacheWrite1hPer1MUsd: 11
17780
18002
  },
17781
18003
  "eu.anthropic.claude-opus-5": {
17782
18004
  inputPer1MUsd: 5.5,
17783
18005
  outputPer1MUsd: 27.5,
17784
18006
  cacheReadPer1MUsd: 0.55,
17785
- cacheWritePer1MUsd: 6.875
18007
+ cacheWritePer1MUsd: 6.875,
18008
+ cacheWrite1hPer1MUsd: 11
17786
18009
  },
17787
18010
  "eu.anthropic.claude-opus-5-5": {
17788
18011
  inputPer1MUsd: 4.4,
17789
18012
  outputPer1MUsd: 22,
17790
18013
  cacheReadPer1MUsd: 0.22,
17791
- cacheWritePer1MUsd: 5.5
18014
+ cacheWritePer1MUsd: 5.5,
18015
+ cacheWrite1hPer1MUsd: 8.8
17792
18016
  },
17793
18017
  "eu.anthropic.claude-sonnet-4-20250514-v1:0": {
17794
18018
  inputPer1MUsd: 3,
@@ -17800,25 +18024,29 @@ var litellm_pricing_snapshot_default = {
17800
18024
  inputPer1MUsd: 3.3000000000000003,
17801
18025
  outputPer1MUsd: 16.5,
17802
18026
  cacheReadPer1MUsd: 0.33,
17803
- cacheWritePer1MUsd: 4.125
18027
+ cacheWritePer1MUsd: 4.125,
18028
+ cacheWrite1hPer1MUsd: 6.6000000000000005
17804
18029
  },
17805
18030
  "eu.anthropic.claude-sonnet-4-6": {
17806
18031
  inputPer1MUsd: 3.3000000000000003,
17807
18032
  outputPer1MUsd: 16.5,
17808
18033
  cacheReadPer1MUsd: 0.33,
17809
- cacheWritePer1MUsd: 4.125
18034
+ cacheWritePer1MUsd: 4.125,
18035
+ cacheWrite1hPer1MUsd: 6.6000000000000005
17810
18036
  },
17811
18037
  "eu.anthropic.claude-sonnet-5": {
17812
18038
  inputPer1MUsd: 2.2,
17813
18039
  outputPer1MUsd: 11,
17814
18040
  cacheReadPer1MUsd: 0.22,
17815
- cacheWritePer1MUsd: 2.75
18041
+ cacheWritePer1MUsd: 2.75,
18042
+ cacheWrite1hPer1MUsd: 4.4
17816
18043
  },
17817
18044
  "eu.anthropic.claude-sonnet-5-5": {
17818
18045
  inputPer1MUsd: 2.2,
17819
18046
  outputPer1MUsd: 11,
17820
18047
  cacheReadPer1MUsd: 0.22,
17821
- cacheWritePer1MUsd: 2.75
18048
+ cacheWritePer1MUsd: 2.75,
18049
+ cacheWrite1hPer1MUsd: 4.4
17822
18050
  },
17823
18051
  "eu.deepseek.v3.2": {
17824
18052
  inputPer1MUsd: 0.74,
@@ -19993,67 +20221,78 @@ var litellm_pricing_snapshot_default = {
19993
20221
  inputPer1MUsd: 10,
19994
20222
  outputPer1MUsd: 50,
19995
20223
  cacheReadPer1MUsd: 1,
19996
- cacheWritePer1MUsd: 12.5
20224
+ cacheWritePer1MUsd: 12.5,
20225
+ cacheWrite1hPer1MUsd: 20
19997
20226
  },
19998
20227
  "global.anthropic.claude-fable-5-1": {
19999
20228
  inputPer1MUsd: 10,
20000
20229
  outputPer1MUsd: 50,
20001
20230
  cacheReadPer1MUsd: 0.25,
20002
- cacheWritePer1MUsd: 12.5
20231
+ cacheWritePer1MUsd: 12.5,
20232
+ cacheWrite1hPer1MUsd: 20
20003
20233
  },
20004
20234
  "global.anthropic.claude-haiku-4-5-20251001-v1:0": {
20005
20235
  inputPer1MUsd: 1,
20006
20236
  outputPer1MUsd: 5,
20007
20237
  cacheReadPer1MUsd: 0.09999999999999999,
20008
- cacheWritePer1MUsd: 1.25
20238
+ cacheWritePer1MUsd: 1.25,
20239
+ cacheWrite1hPer1MUsd: 2
20009
20240
  },
20010
20241
  "global.anthropic.claude-haiku-5-5": {
20011
20242
  inputPer1MUsd: 0.09999999999999999,
20012
20243
  outputPer1MUsd: 0.5,
20013
20244
  cacheReadPer1MUsd: 0.01,
20014
- cacheWritePer1MUsd: 0.125
20245
+ cacheWritePer1MUsd: 0.125,
20246
+ cacheWrite1hPer1MUsd: 0.19999999999999998
20015
20247
  },
20016
20248
  "global.anthropic.claude-mythos-5-1": {
20017
20249
  inputPer1MUsd: 10,
20018
20250
  outputPer1MUsd: 50,
20019
20251
  cacheReadPer1MUsd: 0.25,
20020
- cacheWritePer1MUsd: 12.5
20252
+ cacheWritePer1MUsd: 12.5,
20253
+ cacheWrite1hPer1MUsd: 20
20021
20254
  },
20022
20255
  "global.anthropic.claude-opus-4-5-20251101-v1:0": {
20023
20256
  inputPer1MUsd: 5,
20024
20257
  outputPer1MUsd: 25,
20025
20258
  cacheReadPer1MUsd: 0.5,
20026
- cacheWritePer1MUsd: 6.25
20259
+ cacheWritePer1MUsd: 6.25,
20260
+ cacheWrite1hPer1MUsd: 10
20027
20261
  },
20028
20262
  "global.anthropic.claude-opus-4-6-v1": {
20029
20263
  inputPer1MUsd: 5,
20030
20264
  outputPer1MUsd: 25,
20031
20265
  cacheReadPer1MUsd: 0.5,
20032
- cacheWritePer1MUsd: 6.25
20266
+ cacheWritePer1MUsd: 6.25,
20267
+ cacheWrite1hPer1MUsd: 10
20033
20268
  },
20034
20269
  "global.anthropic.claude-opus-4-7": {
20035
20270
  inputPer1MUsd: 5,
20036
20271
  outputPer1MUsd: 25,
20037
20272
  cacheReadPer1MUsd: 0.5,
20038
- cacheWritePer1MUsd: 6.25
20273
+ cacheWritePer1MUsd: 6.25,
20274
+ cacheWrite1hPer1MUsd: 10
20039
20275
  },
20040
20276
  "global.anthropic.claude-opus-4-8": {
20041
20277
  inputPer1MUsd: 5,
20042
20278
  outputPer1MUsd: 25,
20043
20279
  cacheReadPer1MUsd: 0.5,
20044
- cacheWritePer1MUsd: 6.25
20280
+ cacheWritePer1MUsd: 6.25,
20281
+ cacheWrite1hPer1MUsd: 10
20045
20282
  },
20046
20283
  "global.anthropic.claude-opus-5": {
20047
20284
  inputPer1MUsd: 5,
20048
20285
  outputPer1MUsd: 25,
20049
20286
  cacheReadPer1MUsd: 0.5,
20050
- cacheWritePer1MUsd: 6.25
20287
+ cacheWritePer1MUsd: 6.25,
20288
+ cacheWrite1hPer1MUsd: 10
20051
20289
  },
20052
20290
  "global.anthropic.claude-opus-5-5": {
20053
20291
  inputPer1MUsd: 4,
20054
20292
  outputPer1MUsd: 20,
20055
20293
  cacheReadPer1MUsd: 0.19999999999999998,
20056
- cacheWritePer1MUsd: 5
20294
+ cacheWritePer1MUsd: 5,
20295
+ cacheWrite1hPer1MUsd: 8
20057
20296
  },
20058
20297
  "global.anthropic.claude-sonnet-4-20250514-v1:0": {
20059
20298
  inputPer1MUsd: 3,
@@ -20065,25 +20304,29 @@ var litellm_pricing_snapshot_default = {
20065
20304
  inputPer1MUsd: 3,
20066
20305
  outputPer1MUsd: 15,
20067
20306
  cacheReadPer1MUsd: 0.3,
20068
- cacheWritePer1MUsd: 3.75
20307
+ cacheWritePer1MUsd: 3.75,
20308
+ cacheWrite1hPer1MUsd: 6
20069
20309
  },
20070
20310
  "global.anthropic.claude-sonnet-4-6": {
20071
20311
  inputPer1MUsd: 3,
20072
20312
  outputPer1MUsd: 15,
20073
20313
  cacheReadPer1MUsd: 0.3,
20074
- cacheWritePer1MUsd: 3.75
20314
+ cacheWritePer1MUsd: 3.75,
20315
+ cacheWrite1hPer1MUsd: 6
20075
20316
  },
20076
20317
  "global.anthropic.claude-sonnet-5": {
20077
20318
  inputPer1MUsd: 2,
20078
20319
  outputPer1MUsd: 10,
20079
20320
  cacheReadPer1MUsd: 0.19999999999999998,
20080
- cacheWritePer1MUsd: 2.5
20321
+ cacheWritePer1MUsd: 2.5,
20322
+ cacheWrite1hPer1MUsd: 4
20081
20323
  },
20082
20324
  "global.anthropic.claude-sonnet-5-5": {
20083
20325
  inputPer1MUsd: 2,
20084
20326
  outputPer1MUsd: 10,
20085
20327
  cacheReadPer1MUsd: 0.19999999999999998,
20086
- cacheWritePer1MUsd: 2.5
20328
+ cacheWritePer1MUsd: 2.5,
20329
+ cacheWrite1hPer1MUsd: 4
20087
20330
  },
20088
20331
  "global.cohere.embed-v4:0": {
20089
20332
  inputPer1MUsd: 0.12,
@@ -20941,61 +21184,71 @@ var litellm_pricing_snapshot_default = {
20941
21184
  inputPer1MUsd: 1.1,
20942
21185
  outputPer1MUsd: 5.5,
20943
21186
  cacheReadPer1MUsd: 0.11,
20944
- cacheWritePer1MUsd: 1.375
21187
+ cacheWritePer1MUsd: 1.375,
21188
+ cacheWrite1hPer1MUsd: 2.2
20945
21189
  },
20946
21190
  "jp.anthropic.claude-haiku-5-5": {
20947
21191
  inputPer1MUsd: 0.11,
20948
21192
  outputPer1MUsd: 0.55,
20949
21193
  cacheReadPer1MUsd: 0.011,
20950
- cacheWritePer1MUsd: 0.1375
21194
+ cacheWritePer1MUsd: 0.1375,
21195
+ cacheWrite1hPer1MUsd: 0.22
20951
21196
  },
20952
21197
  "jp.anthropic.claude-opus-4-7": {
20953
21198
  inputPer1MUsd: 5.5,
20954
21199
  outputPer1MUsd: 27.5,
20955
21200
  cacheReadPer1MUsd: 0.55,
20956
- cacheWritePer1MUsd: 6.875
21201
+ cacheWritePer1MUsd: 6.875,
21202
+ cacheWrite1hPer1MUsd: 11
20957
21203
  },
20958
21204
  "jp.anthropic.claude-opus-4-8": {
20959
21205
  inputPer1MUsd: 5.5,
20960
21206
  outputPer1MUsd: 27.5,
20961
21207
  cacheReadPer1MUsd: 0.55,
20962
- cacheWritePer1MUsd: 6.875
21208
+ cacheWritePer1MUsd: 6.875,
21209
+ cacheWrite1hPer1MUsd: 11
20963
21210
  },
20964
21211
  "jp.anthropic.claude-opus-5": {
20965
21212
  inputPer1MUsd: 5.5,
20966
21213
  outputPer1MUsd: 27.5,
20967
21214
  cacheReadPer1MUsd: 0.55,
20968
- cacheWritePer1MUsd: 6.875
21215
+ cacheWritePer1MUsd: 6.875,
21216
+ cacheWrite1hPer1MUsd: 11
20969
21217
  },
20970
21218
  "jp.anthropic.claude-opus-5-5": {
20971
21219
  inputPer1MUsd: 4.4,
20972
21220
  outputPer1MUsd: 22,
20973
21221
  cacheReadPer1MUsd: 0.22,
20974
- cacheWritePer1MUsd: 5.5
21222
+ cacheWritePer1MUsd: 5.5,
21223
+ cacheWrite1hPer1MUsd: 8.8
20975
21224
  },
20976
21225
  "jp.anthropic.claude-sonnet-4-5-20250929-v1:0": {
20977
21226
  inputPer1MUsd: 3.3000000000000003,
20978
21227
  outputPer1MUsd: 16.5,
20979
21228
  cacheReadPer1MUsd: 0.33,
20980
- cacheWritePer1MUsd: 4.125
21229
+ cacheWritePer1MUsd: 4.125,
21230
+ cacheWrite1hPer1MUsd: 6.6000000000000005
20981
21231
  },
20982
21232
  "jp.anthropic.claude-sonnet-4-6": {
20983
21233
  inputPer1MUsd: 3.3000000000000003,
20984
21234
  outputPer1MUsd: 16.5,
20985
21235
  cacheReadPer1MUsd: 0.33,
20986
- cacheWritePer1MUsd: 4.125
21236
+ cacheWritePer1MUsd: 4.125,
21237
+ cacheWrite1hPer1MUsd: 6.6000000000000005
20987
21238
  },
20988
21239
  "jp.anthropic.claude-sonnet-5": {
20989
21240
  inputPer1MUsd: 2.2,
20990
21241
  outputPer1MUsd: 11,
20991
21242
  cacheReadPer1MUsd: 0.22,
20992
- cacheWritePer1MUsd: 2.75
21243
+ cacheWritePer1MUsd: 2.75,
21244
+ cacheWrite1hPer1MUsd: 4.4
20993
21245
  },
20994
21246
  "jp.anthropic.claude-sonnet-5-5": {
20995
21247
  inputPer1MUsd: 2.2,
20996
21248
  outputPer1MUsd: 11,
20997
21249
  cacheReadPer1MUsd: 0.22,
20998
- cacheWritePer1MUsd: 2.75
21250
+ cacheWritePer1MUsd: 2.75,
21251
+ cacheWrite1hPer1MUsd: 4.4
20999
21252
  },
21000
21253
  "kimi-k2-thinking-251104": {
21001
21254
  inputPer1MUsd: 0,
@@ -27198,49 +27451,57 @@ var litellm_pricing_snapshot_default = {
27198
27451
  inputPer1MUsd: 12,
27199
27452
  outputPer1MUsd: 60,
27200
27453
  cacheReadPer1MUsd: 0.3,
27201
- cacheWritePer1MUsd: 15
27454
+ cacheWritePer1MUsd: 15,
27455
+ cacheWrite1hPer1MUsd: 24
27202
27456
  },
27203
27457
  "us-gov.anthropic.claude-haiku-5-5": {
27204
27458
  inputPer1MUsd: 0.12,
27205
27459
  outputPer1MUsd: 0.6,
27206
27460
  cacheReadPer1MUsd: 0.012,
27207
- cacheWritePer1MUsd: 0.15
27461
+ cacheWritePer1MUsd: 0.15,
27462
+ cacheWrite1hPer1MUsd: 0.24
27208
27463
  },
27209
27464
  "us-gov.anthropic.claude-opus-4-8": {
27210
27465
  inputPer1MUsd: 6,
27211
27466
  outputPer1MUsd: 30,
27212
27467
  cacheReadPer1MUsd: 0.6,
27213
- cacheWritePer1MUsd: 7.5
27468
+ cacheWritePer1MUsd: 7.5,
27469
+ cacheWrite1hPer1MUsd: 12
27214
27470
  },
27215
27471
  "us-gov.anthropic.claude-opus-5": {
27216
27472
  inputPer1MUsd: 6,
27217
27473
  outputPer1MUsd: 30,
27218
27474
  cacheReadPer1MUsd: 0.6,
27219
- cacheWritePer1MUsd: 7.5
27475
+ cacheWritePer1MUsd: 7.5,
27476
+ cacheWrite1hPer1MUsd: 12
27220
27477
  },
27221
27478
  "us-gov.anthropic.claude-opus-5-5": {
27222
27479
  inputPer1MUsd: 4.8,
27223
27480
  outputPer1MUsd: 24,
27224
27481
  cacheReadPer1MUsd: 0.24,
27225
- cacheWritePer1MUsd: 6
27482
+ cacheWritePer1MUsd: 6,
27483
+ cacheWrite1hPer1MUsd: 9.6
27226
27484
  },
27227
27485
  "us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0": {
27228
27486
  inputPer1MUsd: 3.5999999999999996,
27229
27487
  outputPer1MUsd: 18,
27230
27488
  cacheReadPer1MUsd: 0.36,
27231
- cacheWritePer1MUsd: 4.5
27489
+ cacheWritePer1MUsd: 4.5,
27490
+ cacheWrite1hPer1MUsd: 7.199999999999999
27232
27491
  },
27233
27492
  "us-gov.anthropic.claude-sonnet-5": {
27234
27493
  inputPer1MUsd: 2.4,
27235
27494
  outputPer1MUsd: 12,
27236
27495
  cacheReadPer1MUsd: 0.24,
27237
- cacheWritePer1MUsd: 3
27496
+ cacheWritePer1MUsd: 3,
27497
+ cacheWrite1hPer1MUsd: 4.8
27238
27498
  },
27239
27499
  "us-gov.anthropic.claude-sonnet-5-5": {
27240
27500
  inputPer1MUsd: 2.4,
27241
27501
  outputPer1MUsd: 12,
27242
27502
  cacheReadPer1MUsd: 0.24,
27243
- cacheWritePer1MUsd: 3
27503
+ cacheWritePer1MUsd: 3,
27504
+ cacheWrite1hPer1MUsd: 4.8
27244
27505
  },
27245
27506
  "us-gov.nvidia.nemotron-nano-12b-v2": {
27246
27507
  inputPer1MUsd: 0.24,
@@ -27330,43 +27591,50 @@ var litellm_pricing_snapshot_default = {
27330
27591
  inputPer1MUsd: 11,
27331
27592
  outputPer1MUsd: 55,
27332
27593
  cacheReadPer1MUsd: 1.1,
27333
- cacheWritePer1MUsd: 13.75
27594
+ cacheWritePer1MUsd: 13.75,
27595
+ cacheWrite1hPer1MUsd: 22
27334
27596
  },
27335
27597
  "us.anthropic.claude-fable-5-1": {
27336
27598
  inputPer1MUsd: 11,
27337
27599
  outputPer1MUsd: 55,
27338
27600
  cacheReadPer1MUsd: 0.275,
27339
- cacheWritePer1MUsd: 13.75
27601
+ cacheWritePer1MUsd: 13.75,
27602
+ cacheWrite1hPer1MUsd: 22
27340
27603
  },
27341
27604
  "us.anthropic.claude-haiku-4-5-20251001-v1:0": {
27342
27605
  inputPer1MUsd: 1.1,
27343
27606
  outputPer1MUsd: 5.5,
27344
27607
  cacheReadPer1MUsd: 0.11,
27345
- cacheWritePer1MUsd: 1.375
27608
+ cacheWritePer1MUsd: 1.375,
27609
+ cacheWrite1hPer1MUsd: 2.2
27346
27610
  },
27347
27611
  "us.anthropic.claude-haiku-5-5": {
27348
27612
  inputPer1MUsd: 0.11,
27349
27613
  outputPer1MUsd: 0.55,
27350
27614
  cacheReadPer1MUsd: 0.011,
27351
- cacheWritePer1MUsd: 0.1375
27615
+ cacheWritePer1MUsd: 0.1375,
27616
+ cacheWrite1hPer1MUsd: 0.22
27352
27617
  },
27353
27618
  "us.anthropic.claude-mythos-5": {
27354
27619
  inputPer1MUsd: 11,
27355
27620
  outputPer1MUsd: 55,
27356
27621
  cacheReadPer1MUsd: 1.1,
27357
- cacheWritePer1MUsd: 13.75
27622
+ cacheWritePer1MUsd: 13.75,
27623
+ cacheWrite1hPer1MUsd: 22
27358
27624
  },
27359
27625
  "us.anthropic.claude-mythos-5-1": {
27360
27626
  inputPer1MUsd: 11,
27361
27627
  outputPer1MUsd: 55,
27362
27628
  cacheReadPer1MUsd: 0.275,
27363
- cacheWritePer1MUsd: 13.75
27629
+ cacheWritePer1MUsd: 13.75,
27630
+ cacheWrite1hPer1MUsd: 22
27364
27631
  },
27365
27632
  "us.anthropic.claude-mythos-preview": {
27366
27633
  inputPer1MUsd: 27.5,
27367
27634
  outputPer1MUsd: 137.5,
27368
27635
  cacheReadPer1MUsd: 2.75,
27369
- cacheWritePer1MUsd: 34.375
27636
+ cacheWritePer1MUsd: 34.375,
27637
+ cacheWrite1hPer1MUsd: 55
27370
27638
  },
27371
27639
  "us.anthropic.claude-opus-4-1-20250805-v1:0": {
27372
27640
  inputPer1MUsd: 15,
@@ -27384,37 +27652,43 @@ var litellm_pricing_snapshot_default = {
27384
27652
  inputPer1MUsd: 5.5,
27385
27653
  outputPer1MUsd: 27.5,
27386
27654
  cacheReadPer1MUsd: 0.55,
27387
- cacheWritePer1MUsd: 6.875
27655
+ cacheWritePer1MUsd: 6.875,
27656
+ cacheWrite1hPer1MUsd: 11
27388
27657
  },
27389
27658
  "us.anthropic.claude-opus-4-6-v1": {
27390
27659
  inputPer1MUsd: 5.5,
27391
27660
  outputPer1MUsd: 27.5,
27392
27661
  cacheReadPer1MUsd: 0.55,
27393
- cacheWritePer1MUsd: 6.875
27662
+ cacheWritePer1MUsd: 6.875,
27663
+ cacheWrite1hPer1MUsd: 11
27394
27664
  },
27395
27665
  "us.anthropic.claude-opus-4-7": {
27396
27666
  inputPer1MUsd: 5.5,
27397
27667
  outputPer1MUsd: 27.5,
27398
27668
  cacheReadPer1MUsd: 0.55,
27399
- cacheWritePer1MUsd: 6.875
27669
+ cacheWritePer1MUsd: 6.875,
27670
+ cacheWrite1hPer1MUsd: 11
27400
27671
  },
27401
27672
  "us.anthropic.claude-opus-4-8": {
27402
27673
  inputPer1MUsd: 5.5,
27403
27674
  outputPer1MUsd: 27.5,
27404
27675
  cacheReadPer1MUsd: 0.55,
27405
- cacheWritePer1MUsd: 6.875
27676
+ cacheWritePer1MUsd: 6.875,
27677
+ cacheWrite1hPer1MUsd: 11
27406
27678
  },
27407
27679
  "us.anthropic.claude-opus-5": {
27408
27680
  inputPer1MUsd: 5.5,
27409
27681
  outputPer1MUsd: 27.5,
27410
27682
  cacheReadPer1MUsd: 0.55,
27411
- cacheWritePer1MUsd: 6.875
27683
+ cacheWritePer1MUsd: 6.875,
27684
+ cacheWrite1hPer1MUsd: 11
27412
27685
  },
27413
27686
  "us.anthropic.claude-opus-5-5": {
27414
27687
  inputPer1MUsd: 4.4,
27415
27688
  outputPer1MUsd: 22,
27416
27689
  cacheReadPer1MUsd: 0.22,
27417
- cacheWritePer1MUsd: 5.5
27690
+ cacheWritePer1MUsd: 5.5,
27691
+ cacheWrite1hPer1MUsd: 8.8
27418
27692
  },
27419
27693
  "us.anthropic.claude-sonnet-4-20250514-v1:0": {
27420
27694
  inputPer1MUsd: 3,
@@ -27426,25 +27700,29 @@ var litellm_pricing_snapshot_default = {
27426
27700
  inputPer1MUsd: 3.3000000000000003,
27427
27701
  outputPer1MUsd: 16.5,
27428
27702
  cacheReadPer1MUsd: 0.33,
27429
- cacheWritePer1MUsd: 4.125
27703
+ cacheWritePer1MUsd: 4.125,
27704
+ cacheWrite1hPer1MUsd: 6.6000000000000005
27430
27705
  },
27431
27706
  "us.anthropic.claude-sonnet-4-6": {
27432
27707
  inputPer1MUsd: 3.3000000000000003,
27433
27708
  outputPer1MUsd: 16.5,
27434
27709
  cacheReadPer1MUsd: 0.33,
27435
- cacheWritePer1MUsd: 4.125
27710
+ cacheWritePer1MUsd: 4.125,
27711
+ cacheWrite1hPer1MUsd: 6.6000000000000005
27436
27712
  },
27437
27713
  "us.anthropic.claude-sonnet-5": {
27438
27714
  inputPer1MUsd: 2.2,
27439
27715
  outputPer1MUsd: 11,
27440
27716
  cacheReadPer1MUsd: 0.22,
27441
- cacheWritePer1MUsd: 2.75
27717
+ cacheWritePer1MUsd: 2.75,
27718
+ cacheWrite1hPer1MUsd: 4.4
27442
27719
  },
27443
27720
  "us.anthropic.claude-sonnet-5-5": {
27444
27721
  inputPer1MUsd: 2.2,
27445
27722
  outputPer1MUsd: 11,
27446
27723
  cacheReadPer1MUsd: 0.22,
27447
- cacheWritePer1MUsd: 2.75
27724
+ cacheWritePer1MUsd: 2.75,
27725
+ cacheWrite1hPer1MUsd: 4.4
27448
27726
  },
27449
27727
  "us.cohere.embed-v4:0": {
27450
27728
  inputPer1MUsd: 0.12,
@@ -28091,169 +28369,197 @@ var litellm_pricing_snapshot_default = {
28091
28369
  inputPer1MUsd: 10,
28092
28370
  outputPer1MUsd: 50,
28093
28371
  cacheReadPer1MUsd: 1,
28094
- cacheWritePer1MUsd: 12.5
28372
+ cacheWritePer1MUsd: 12.5,
28373
+ cacheWrite1hPer1MUsd: 20
28095
28374
  },
28096
28375
  "vertex_ai/claude-fable-5-1": {
28097
28376
  inputPer1MUsd: 10,
28098
28377
  outputPer1MUsd: 50,
28099
28378
  cacheReadPer1MUsd: 0.25,
28100
- cacheWritePer1MUsd: 12.5
28379
+ cacheWritePer1MUsd: 12.5,
28380
+ cacheWrite1hPer1MUsd: 20
28101
28381
  },
28102
28382
  "vertex_ai/claude-fable-5-1@default": {
28103
28383
  inputPer1MUsd: 10,
28104
28384
  outputPer1MUsd: 50,
28105
28385
  cacheReadPer1MUsd: 0.25,
28106
- cacheWritePer1MUsd: 12.5
28386
+ cacheWritePer1MUsd: 12.5,
28387
+ cacheWrite1hPer1MUsd: 20
28107
28388
  },
28108
28389
  "vertex_ai/claude-fable-5@default": {
28109
28390
  inputPer1MUsd: 10,
28110
28391
  outputPer1MUsd: 50,
28111
28392
  cacheReadPer1MUsd: 1,
28112
- cacheWritePer1MUsd: 12.5
28393
+ cacheWritePer1MUsd: 12.5,
28394
+ cacheWrite1hPer1MUsd: 20
28113
28395
  },
28114
28396
  "vertex_ai/claude-haiku-4-5": {
28115
28397
  inputPer1MUsd: 1,
28116
28398
  outputPer1MUsd: 5,
28117
28399
  cacheReadPer1MUsd: 0.09999999999999999,
28118
- cacheWritePer1MUsd: 1.25
28400
+ cacheWritePer1MUsd: 1.25,
28401
+ cacheWrite1hPer1MUsd: 2
28119
28402
  },
28120
28403
  "vertex_ai/claude-haiku-4-5@20251001": {
28121
28404
  inputPer1MUsd: 1,
28122
28405
  outputPer1MUsd: 5,
28123
28406
  cacheReadPer1MUsd: 0.09999999999999999,
28124
- cacheWritePer1MUsd: 1.25
28407
+ cacheWritePer1MUsd: 1.25,
28408
+ cacheWrite1hPer1MUsd: 2
28125
28409
  },
28126
28410
  "vertex_ai/claude-haiku-5-5": {
28127
28411
  inputPer1MUsd: 0.09999999999999999,
28128
28412
  outputPer1MUsd: 0.5,
28129
28413
  cacheReadPer1MUsd: 0.01,
28130
- cacheWritePer1MUsd: 0.125
28414
+ cacheWritePer1MUsd: 0.125,
28415
+ cacheWrite1hPer1MUsd: 0.19999999999999998
28131
28416
  },
28132
28417
  "vertex_ai/claude-haiku-5-5@default": {
28133
28418
  inputPer1MUsd: 0.09999999999999999,
28134
28419
  outputPer1MUsd: 0.5,
28135
28420
  cacheReadPer1MUsd: 0.01,
28136
- cacheWritePer1MUsd: 0.125
28421
+ cacheWritePer1MUsd: 0.125,
28422
+ cacheWrite1hPer1MUsd: 0.19999999999999998
28137
28423
  },
28138
28424
  "vertex_ai/claude-opus-4-5": {
28139
28425
  inputPer1MUsd: 5,
28140
28426
  outputPer1MUsd: 25,
28141
28427
  cacheReadPer1MUsd: 0.5,
28142
- cacheWritePer1MUsd: 6.25
28428
+ cacheWritePer1MUsd: 6.25,
28429
+ cacheWrite1hPer1MUsd: 10
28143
28430
  },
28144
28431
  "vertex_ai/claude-opus-4-5@20251101": {
28145
28432
  inputPer1MUsd: 5,
28146
28433
  outputPer1MUsd: 25,
28147
28434
  cacheReadPer1MUsd: 0.5,
28148
- cacheWritePer1MUsd: 6.25
28435
+ cacheWritePer1MUsd: 6.25,
28436
+ cacheWrite1hPer1MUsd: 10
28149
28437
  },
28150
28438
  "vertex_ai/claude-opus-4-6": {
28151
28439
  inputPer1MUsd: 5,
28152
28440
  outputPer1MUsd: 25,
28153
28441
  cacheReadPer1MUsd: 0.5,
28154
- cacheWritePer1MUsd: 6.25
28442
+ cacheWritePer1MUsd: 6.25,
28443
+ cacheWrite1hPer1MUsd: 10
28155
28444
  },
28156
28445
  "vertex_ai/claude-opus-4-6@default": {
28157
28446
  inputPer1MUsd: 5,
28158
28447
  outputPer1MUsd: 25,
28159
28448
  cacheReadPer1MUsd: 0.5,
28160
- cacheWritePer1MUsd: 6.25
28449
+ cacheWritePer1MUsd: 6.25,
28450
+ cacheWrite1hPer1MUsd: 10
28161
28451
  },
28162
28452
  "vertex_ai/claude-opus-4-7": {
28163
28453
  inputPer1MUsd: 5,
28164
28454
  outputPer1MUsd: 25,
28165
28455
  cacheReadPer1MUsd: 0.5,
28166
- cacheWritePer1MUsd: 6.25
28456
+ cacheWritePer1MUsd: 6.25,
28457
+ cacheWrite1hPer1MUsd: 10
28167
28458
  },
28168
28459
  "vertex_ai/claude-opus-4-7@default": {
28169
28460
  inputPer1MUsd: 5,
28170
28461
  outputPer1MUsd: 25,
28171
28462
  cacheReadPer1MUsd: 0.5,
28172
- cacheWritePer1MUsd: 6.25
28463
+ cacheWritePer1MUsd: 6.25,
28464
+ cacheWrite1hPer1MUsd: 10
28173
28465
  },
28174
28466
  "vertex_ai/claude-opus-4-8": {
28175
28467
  inputPer1MUsd: 5,
28176
28468
  outputPer1MUsd: 25,
28177
28469
  cacheReadPer1MUsd: 0.5,
28178
- cacheWritePer1MUsd: 6.25
28470
+ cacheWritePer1MUsd: 6.25,
28471
+ cacheWrite1hPer1MUsd: 10
28179
28472
  },
28180
28473
  "vertex_ai/claude-opus-4-8@default": {
28181
28474
  inputPer1MUsd: 5,
28182
28475
  outputPer1MUsd: 25,
28183
28476
  cacheReadPer1MUsd: 0.5,
28184
- cacheWritePer1MUsd: 6.25
28477
+ cacheWritePer1MUsd: 6.25,
28478
+ cacheWrite1hPer1MUsd: 10
28185
28479
  },
28186
28480
  "vertex_ai/claude-opus-5": {
28187
28481
  inputPer1MUsd: 5,
28188
28482
  outputPer1MUsd: 25,
28189
28483
  cacheReadPer1MUsd: 0.5,
28190
- cacheWritePer1MUsd: 6.25
28484
+ cacheWritePer1MUsd: 6.25,
28485
+ cacheWrite1hPer1MUsd: 10
28191
28486
  },
28192
28487
  "vertex_ai/claude-opus-5-5": {
28193
28488
  inputPer1MUsd: 4,
28194
28489
  outputPer1MUsd: 20,
28195
28490
  cacheReadPer1MUsd: 0.19999999999999998,
28196
- cacheWritePer1MUsd: 5
28491
+ cacheWritePer1MUsd: 5,
28492
+ cacheWrite1hPer1MUsd: 8
28197
28493
  },
28198
28494
  "vertex_ai/claude-opus-5-5@default": {
28199
28495
  inputPer1MUsd: 4,
28200
28496
  outputPer1MUsd: 20,
28201
28497
  cacheReadPer1MUsd: 0.19999999999999998,
28202
- cacheWritePer1MUsd: 5
28498
+ cacheWritePer1MUsd: 5,
28499
+ cacheWrite1hPer1MUsd: 8
28203
28500
  },
28204
28501
  "vertex_ai/claude-opus-5@default": {
28205
28502
  inputPer1MUsd: 5,
28206
28503
  outputPer1MUsd: 25,
28207
28504
  cacheReadPer1MUsd: 0.5,
28208
- cacheWritePer1MUsd: 6.25
28505
+ cacheWritePer1MUsd: 6.25,
28506
+ cacheWrite1hPer1MUsd: 10
28209
28507
  },
28210
28508
  "vertex_ai/claude-sonnet-4-5": {
28211
28509
  inputPer1MUsd: 3,
28212
28510
  outputPer1MUsd: 15,
28213
28511
  cacheReadPer1MUsd: 0.3,
28214
- cacheWritePer1MUsd: 3.75
28512
+ cacheWritePer1MUsd: 3.75,
28513
+ cacheWrite1hPer1MUsd: 6
28215
28514
  },
28216
28515
  "vertex_ai/claude-sonnet-4-5@20250929": {
28217
28516
  inputPer1MUsd: 3,
28218
28517
  outputPer1MUsd: 15,
28219
28518
  cacheReadPer1MUsd: 0.3,
28220
- cacheWritePer1MUsd: 3.75
28519
+ cacheWritePer1MUsd: 3.75,
28520
+ cacheWrite1hPer1MUsd: 6
28221
28521
  },
28222
28522
  "vertex_ai/claude-sonnet-4-6": {
28223
28523
  inputPer1MUsd: 3,
28224
28524
  outputPer1MUsd: 15,
28225
28525
  cacheReadPer1MUsd: 0.3,
28226
- cacheWritePer1MUsd: 3.75
28526
+ cacheWritePer1MUsd: 3.75,
28527
+ cacheWrite1hPer1MUsd: 6
28227
28528
  },
28228
28529
  "vertex_ai/claude-sonnet-4-6@default": {
28229
28530
  inputPer1MUsd: 3,
28230
28531
  outputPer1MUsd: 15,
28231
28532
  cacheReadPer1MUsd: 0.3,
28232
- cacheWritePer1MUsd: 3.75
28533
+ cacheWritePer1MUsd: 3.75,
28534
+ cacheWrite1hPer1MUsd: 6
28233
28535
  },
28234
28536
  "vertex_ai/claude-sonnet-5": {
28235
28537
  inputPer1MUsd: 2,
28236
28538
  outputPer1MUsd: 10,
28237
28539
  cacheReadPer1MUsd: 0.19999999999999998,
28238
- cacheWritePer1MUsd: 2.5
28540
+ cacheWritePer1MUsd: 2.5,
28541
+ cacheWrite1hPer1MUsd: 4
28239
28542
  },
28240
28543
  "vertex_ai/claude-sonnet-5-5": {
28241
28544
  inputPer1MUsd: 2,
28242
28545
  outputPer1MUsd: 10,
28243
28546
  cacheReadPer1MUsd: 0.09999999999999999,
28244
- cacheWritePer1MUsd: 2.5
28547
+ cacheWritePer1MUsd: 2.5,
28548
+ cacheWrite1hPer1MUsd: 4
28245
28549
  },
28246
28550
  "vertex_ai/claude-sonnet-5-5@default": {
28247
28551
  inputPer1MUsd: 2,
28248
28552
  outputPer1MUsd: 10,
28249
28553
  cacheReadPer1MUsd: 0.09999999999999999,
28250
- cacheWritePer1MUsd: 2.5
28554
+ cacheWritePer1MUsd: 2.5,
28555
+ cacheWrite1hPer1MUsd: 4
28251
28556
  },
28252
28557
  "vertex_ai/claude-sonnet-5@default": {
28253
28558
  inputPer1MUsd: 2,
28254
28559
  outputPer1MUsd: 10,
28255
28560
  cacheReadPer1MUsd: 0.19999999999999998,
28256
- cacheWritePer1MUsd: 2.5
28561
+ cacheWritePer1MUsd: 2.5,
28562
+ cacheWrite1hPer1MUsd: 4
28257
28563
  },
28258
28564
  "vertex_ai/codestral-2": {
28259
28565
  inputPer1MUsd: 0.3,
@@ -30570,6 +30876,7 @@ var DEFAULT_FETCH_RETRY_COUNT2 = 2;
30570
30876
  var DEFAULT_FETCH_RETRY_DELAY_MS2 = 200;
30571
30877
  var MAX_LITELLM_PRICING_RESPONSE_BYTES = 33554432;
30572
30878
  var DEFAULT_LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
30879
+ var CACHE_FORMAT_VERSION = 2;
30573
30880
  var RetryableLiteLLMFetchError = class extends Error {
30574
30881
  constructor(message) {
30575
30882
  super(message);
@@ -30622,6 +30929,9 @@ function normalizeModelPricing(rawModelPricing) {
30622
30929
  }
30623
30930
  const cacheReadPerToken = toNonNegativeNumber3(rawModelPricing.cache_read_input_token_cost) ?? toNonNegativeNumber3(rawModelPricing.cache_read_input_token_cost_priority);
30624
30931
  const cacheWritePerToken = toNonNegativeNumber3(rawModelPricing.cache_creation_input_token_cost) ?? toNonNegativeNumber3(rawModelPricing.cache_creation_input_token_cost_priority);
30932
+ const cacheWrite1hPerToken = toNonNegativeNumber3(
30933
+ rawModelPricing.cache_creation_input_token_cost_above_1hr
30934
+ );
30625
30935
  const reasoningPerToken = toNonNegativeNumber3(rawModelPricing.output_cost_per_reasoning_token);
30626
30936
  const modelPricing = {
30627
30937
  inputPer1MUsd: inputPerToken * ONE_MILLION2,
@@ -30633,6 +30943,9 @@ function normalizeModelPricing(rawModelPricing) {
30633
30943
  if (cacheWritePerToken !== void 0) {
30634
30944
  modelPricing.cacheWritePer1MUsd = cacheWritePerToken * ONE_MILLION2;
30635
30945
  }
30946
+ if (cacheWrite1hPerToken !== void 0) {
30947
+ modelPricing.cacheWrite1hPer1MUsd = cacheWrite1hPerToken * ONE_MILLION2;
30948
+ }
30636
30949
  if (reasoningPerToken !== void 0) {
30637
30950
  modelPricing.reasoningPer1MUsd = reasoningPerToken * ONE_MILLION2;
30638
30951
  modelPricing.reasoningBilling = "separate";
@@ -30683,6 +30996,10 @@ function normalizeCachedPricing(rawPricing) {
30683
30996
  if (cacheWritePer1MUsd !== void 0) {
30684
30997
  modelPricing.cacheWritePer1MUsd = cacheWritePer1MUsd;
30685
30998
  }
30999
+ const cacheWrite1hPer1MUsd = toNonNegativeNumber3(pricingRecord.cacheWrite1hPer1MUsd);
31000
+ if (cacheWrite1hPer1MUsd !== void 0) {
31001
+ modelPricing.cacheWrite1hPer1MUsd = cacheWrite1hPer1MUsd;
31002
+ }
30686
31003
  const reasoningPer1MUsd = toNonNegativeNumber3(pricingRecord.reasoningPer1MUsd);
30687
31004
  if (reasoningPer1MUsd !== void 0) {
30688
31005
  modelPricing.reasoningPer1MUsd = reasoningPer1MUsd;
@@ -30927,7 +31244,7 @@ var LiteLLMPricingFetcher = class {
30927
31244
  return false;
30928
31245
  }
30929
31246
  const nowTimestamp = this.now();
30930
- const isStale = cacheFileContent.fetchedAt > nowTimestamp || nowTimestamp - cacheFileContent.fetchedAt > this.cacheTtlMs;
31247
+ const isStale = cacheFileContent.formatVersion !== CACHE_FORMAT_VERSION || cacheFileContent.fetchedAt > nowTimestamp || nowTimestamp - cacheFileContent.fetchedAt > this.cacheTtlMs;
30931
31248
  if (isStale && !options.allowStale) {
30932
31249
  return false;
30933
31250
  }
@@ -30992,12 +31309,14 @@ var LiteLLMPricingFetcher = class {
30992
31309
  } catch {
30993
31310
  return void 0;
30994
31311
  }
30995
- return normalizeLiteLLMCachePayload(parsedPayload);
31312
+ const payload = normalizeLiteLLMCachePayload(parsedPayload);
31313
+ return payload && { ...payload, formatVersion: asRecord(parsedPayload)?.formatVersion };
30996
31314
  }
30997
31315
  async writeCache() {
30998
31316
  const directoryPath = path33.dirname(this.cacheFilePath);
30999
31317
  await ensureDirectory(directoryPath);
31000
31318
  const payload = {
31319
+ formatVersion: CACHE_FORMAT_VERSION,
31001
31320
  fetchedAt: this.now(),
31002
31321
  sourceUrl: this.sourceUrl,
31003
31322
  pricingByModel: Object.fromEntries(this.pricingByModel.entries())
@@ -31117,6 +31436,11 @@ async function resolveAndApplyPricingToEvents(events, options, runtimeConfig = g
31117
31436
  };
31118
31437
  }
31119
31438
 
31439
+ // src/sources/source-adapter.ts
31440
+ function getRepeatingSourceIds(adapters) {
31441
+ return adapters.filter((adapter) => adapter.capabilities?.eventsRepeatAcrossFiles).map((adapter) => adapter.id);
31442
+ }
31443
+
31120
31444
  // src/cli/filter-match-warnings.ts
31121
31445
  function formatSuggestion(input, candidates) {
31122
31446
  const suggestion = suggestClosest(input, candidates);
@@ -31508,6 +31832,7 @@ async function buildUsageEventDataset(options, deps = {}) {
31508
31832
  successfulParseResults,
31509
31833
  discoveredFiles,
31510
31834
  parsedFiles,
31835
+ failedFiles,
31511
31836
  eventStoreAvailable,
31512
31837
  sourceFailures,
31513
31838
  warnings
@@ -31550,10 +31875,11 @@ async function buildUsageEventDataset(options, deps = {}) {
31550
31875
  const parseWarnings = eventStoreOpenWarning ? [eventStoreOpenWarning, ...warnings] : warnings;
31551
31876
  throwOnExplicitSourceFailures(sourceFailures, normalizedInputs.explicitSourceIds);
31552
31877
  let parseResultsForFiltering = successfulParseResults;
31553
- const ledger = eventStoreAvailable ? { path: eventStoreRuntimeConfig.path, countedFiles: [...parsedFiles] } : void 0;
31878
+ const ledger = eventStoreAvailable ? { path: eventStoreRuntimeConfig.path, parsedFiles: [...parsedFiles], historyFiles: [] } : void 0;
31554
31879
  const historyWarnings = [];
31555
31880
  const historyNotes = [];
31556
31881
  const historyStore = openedEventStore;
31882
+ const failedFileKeys = new Set(failedFiles.map((file) => `${file.source}\0${file.filePath}`));
31557
31883
  const historySources = successfulParseResults.map((result) => result.source).filter((source) => historyRequested || !cliDirectorySourceIds.has(source.toLowerCase()));
31558
31884
  if (includeHistory && eventStoreAvailable && historyStore && historySources.length > 0) {
31559
31885
  const loadHistoryEvents2 = deps.loadHistoryEvents ?? loadHistoryEvents;
@@ -31569,7 +31895,13 @@ async function buildUsageEventDataset(options, deps = {}) {
31569
31895
  // Only successfully parsed sources: a failed source has an empty
31570
31896
  // discovered set, so all its stored files would look departed.
31571
31897
  selectedSources: historySources,
31572
- discoveredFiles
31898
+ // A file that failed to parse has not departed, but this run does not
31899
+ // count its stored events, so they cannot suppress a departed copy.
31900
+ discoveredFiles: discoveredFiles.filter(
31901
+ (file) => !failedFileKeys.has(`${file.source}\0${file.filePath}`)
31902
+ ),
31903
+ presentFiles: failedFiles,
31904
+ repeatingSources: getRepeatingSourceIds(adaptersToParse)
31573
31905
  },
31574
31906
  { unverifiable: "treat-as-departed" }
31575
31907
  )
@@ -31579,7 +31911,7 @@ async function buildUsageEventDataset(options, deps = {}) {
31579
31911
  parseResultsForFiltering,
31580
31912
  historyResult.events
31581
31913
  );
31582
- ledger?.countedFiles.push(...historyResult.servedFiles);
31914
+ ledger?.historyFiles.push(...historyResult.servedFiles);
31583
31915
  if (historyRequested || historyResult.servedFileCount > 0) {
31584
31916
  historyNotes.push(formatHistoryNote(historyResult));
31585
31917
  }
@@ -31712,29 +32044,57 @@ async function applyPricingToUsageEventDataset(dataset, deps = {}, pricingLoadMo
31712
32044
  }
31713
32045
 
31714
32046
  // src/cli/build-usage-data.ts
31715
- var DAILY_DEFAULT_DAYS = 7;
31716
- var WEEKLY_DEFAULT_WEEKS = 8;
32047
+ var REPORT_PERIOD_NAMES = {
32048
+ daily: "day",
32049
+ weekly: "week",
32050
+ monthly: "month"
32051
+ };
31717
32052
  var DEFAULT_REPORT_WINDOWS = {
31718
- daily: {
31719
- periods: DAILY_DEFAULT_DAYS,
31720
- period: "day",
31721
- resolveSince: (today) => shiftLocalDateKey(today, -(DAILY_DEFAULT_DAYS - 1))
31722
- },
31723
- weekly: {
31724
- periods: WEEKLY_DEFAULT_WEEKS,
31725
- period: "week",
31726
- resolveSince: (today) => {
32053
+ daily: { periods: 7, period: "day" },
32054
+ weekly: { periods: 8, period: "week" }
32055
+ };
32056
+ var MAX_LAST_PERIODS = 1e4;
32057
+ function parseLastOption(last) {
32058
+ if (last === void 0) {
32059
+ return void 0;
32060
+ }
32061
+ const normalized = last.trim();
32062
+ const periods = Number.parseInt(normalized, 10);
32063
+ if (!/^[1-9]\d*$/u.test(normalized) || periods > MAX_LAST_PERIODS) {
32064
+ throw new Error(`--last must be a whole number from 1 to ${String(MAX_LAST_PERIODS)}`);
32065
+ }
32066
+ return periods;
32067
+ }
32068
+ function resolvePeriodsSince(granularity, today, periods) {
32069
+ switch (granularity) {
32070
+ case "daily":
32071
+ return shiftLocalDateKey(today, -(periods - 1));
32072
+ case "weekly": {
31727
32073
  const currentWeekMonday = shiftLocalDateKey(today, -(getIsoDayOfWeekFromDateKey(today) - 1));
31728
- return shiftLocalDateKey(currentWeekMonday, -7 * (WEEKLY_DEFAULT_WEEKS - 1));
32074
+ return shiftLocalDateKey(currentWeekMonday, -7 * (periods - 1));
32075
+ }
32076
+ case "monthly": {
32077
+ const monthIndex = Number(today.slice(0, 4)) * 12 + Number(today.slice(5, 7)) - 1 - (periods - 1);
32078
+ const year = String(Math.floor(monthIndex / 12)).padStart(4, "0");
32079
+ const month = String(monthIndex % 12 + 1).padStart(2, "0");
32080
+ return `${year}-${month}-01`;
31729
32081
  }
31730
32082
  }
31731
- };
32083
+ }
32084
+ function getReportPeriodName(granularity) {
32085
+ return REPORT_PERIOD_NAMES[granularity];
32086
+ }
31732
32087
  async function resolveUsageDataRequest(granularity, options, deps) {
31733
- if (options.all && (options.since !== void 0 || options.until !== void 0)) {
32088
+ const hasDates = options.since !== void 0 || options.until !== void 0;
32089
+ const lastPeriods = parseLastOption(options.last);
32090
+ if (lastPeriods !== void 0 && (hasDates || options.all)) {
32091
+ throw new Error("--last cannot be combined with --since, --until, or --all");
32092
+ }
32093
+ if (options.all && hasDates) {
31734
32094
  throw new Error("--all cannot be combined with --since or --until");
31735
32095
  }
31736
- const defaultWindow = DEFAULT_REPORT_WINDOWS[granularity];
31737
- if (!defaultWindow || options.all || options.since !== void 0 || options.until !== void 0) {
32096
+ const periods = lastPeriods ?? (options.all || hasDates ? void 0 : DEFAULT_REPORT_WINDOWS[granularity]?.periods);
32097
+ if (periods === void 0) {
31738
32098
  return { options, deps };
31739
32099
  }
31740
32100
  const userConfigResolution = await resolveUserConfigForOptions(options, deps);
@@ -31743,12 +32103,16 @@ async function resolveUsageDataRequest(granularity, options, deps) {
31743
32103
  userConfigResolution.cliOptions
31744
32104
  );
31745
32105
  const today = getCurrentLocalDateKey(timezone, deps.now?.() ?? /* @__PURE__ */ new Date());
31746
- const since = defaultWindow.resolveSince(today);
31747
- const windowedOptions = { ...userConfigResolution.options, since };
32106
+ const since = resolvePeriodsSince(granularity, today, periods);
32107
+ const windowedOptions = {
32108
+ ...userConfigResolution.options,
32109
+ since,
32110
+ ...lastPeriods === void 0 ? {} : { until: today }
32111
+ };
31748
32112
  return {
31749
32113
  options: windowedOptions,
31750
32114
  deps: { ...deps, userConfigResolution: { ...userConfigResolution, options: windowedOptions } },
31751
- defaultWindowSince: since
32115
+ ...lastPeriods === void 0 ? { defaultWindowSince: since } : {}
31752
32116
  };
31753
32117
  }
31754
32118
  async function buildUsageData(granularity, commandOptions, commandDeps = {}) {
@@ -32800,13 +33164,14 @@ function resolveExpandedModelsColumnWidth(bodyRows, tableLayout, currentWidth, m
32800
33164
  }
32801
33165
  return bestWidth;
32802
33166
  }
32803
- function resolveFitSteps(tableLayout, compact) {
32804
- const steps = tableFitSteps.slice(compact ? compactTableFitStepIndex : 0).map(
32805
- (step) => (
32806
- // Per-model metric lines are unreadable without their model names.
32807
- tableLayout === "per_model_columns" ? { ...step, hiddenColumns: step.hiddenColumns.filter((column) => column !== "models") } : step
32808
- )
32809
- );
33167
+ function resolveFitSteps(tableLayout, compact, hideCost) {
33168
+ const steps = tableFitSteps.slice(compact ? compactTableFitStepIndex : 0).map((step) => {
33169
+ const hiddenColumns = tableLayout === "per_model_columns" ? step.hiddenColumns.filter((column) => column !== "models") : step.hiddenColumns;
33170
+ return {
33171
+ ...step,
33172
+ hiddenColumns: hideCost ? [...hiddenColumns, "cost"] : hiddenColumns
33173
+ };
33174
+ });
32810
33175
  return steps.filter((step, index) => {
32811
33176
  const previousStep = steps[index - 1];
32812
33177
  return index === 0 || step.tokenFormat !== previousStep.tokenFormat || step.hiddenColumns.length !== previousStep.hiddenColumns.length;
@@ -32898,7 +33263,7 @@ function renderTerminalTableWithFit(rows, options = {}) {
32898
33263
  const tableLayout = options.tableLayout ?? "compact";
32899
33264
  const hasExplicitTerminalWidth = isValidTerminalWidth(options.terminalWidth);
32900
33265
  const terminalWidth = resolveTerminalWidth(options.terminalWidth);
32901
- const steps = resolveFitSteps(tableLayout, options.compact ?? false);
33266
+ const steps = resolveFitSteps(tableLayout, options.compact ?? false, options.hideCost ?? false);
32902
33267
  let prepared = prepareFitStep(rows, tableLayout, steps[0]);
32903
33268
  if (terminalWidth !== void 0) {
32904
33269
  for (const step of steps.slice(1)) {
@@ -34428,7 +34793,7 @@ async function prepareReport(options) {
34428
34793
  hintsAfterOutput: hintsAfterOutput.length > 0 ? hintsAfterOutput : void 0
34429
34794
  };
34430
34795
  }
34431
- async function writeShareArtifact(artifact, open3) {
34796
+ async function writeShareArtifact(artifact, open4) {
34432
34797
  const svgs = {
34433
34798
  dark: artifact.render(shareThemes.dark),
34434
34799
  light: artifact.render(shareThemes.light)
@@ -34441,7 +34806,7 @@ async function writeShareArtifact(artifact, open3) {
34441
34806
  renderSharePage({ title: artifact.title, fileBaseName, svgs })
34442
34807
  );
34443
34808
  logger.info(`Wrote ${artifact.logLabel} share page: ${pagePath}`);
34444
- if (!open3) {
34809
+ if (!open4) {
34445
34810
  return;
34446
34811
  }
34447
34812
  try {
@@ -36327,6 +36692,9 @@ function createSyntheticEvent(period) {
36327
36692
  reasoningTokens: period.reasoningTokens,
36328
36693
  cacheReadTokens: period.cacheReadTokens,
36329
36694
  cacheWriteTokens: period.cacheWriteTokens,
36695
+ // Report rows do not keep the one-hour share, so candidates price every write at the
36696
+ // standard cache-write rate.
36697
+ cacheWrite1hTokens: 0,
36330
36698
  totalTokens: period.totalTokens,
36331
36699
  costMode: "estimated",
36332
36700
  costUsd: void 0
@@ -37282,17 +37650,20 @@ function formatCost2(period) {
37282
37650
  }
37283
37651
  return `${period.totals.costIncomplete ? "~" : ""}${usdFormatter6.format(period.totals.costUsd)}`;
37284
37652
  }
37653
+ function formatCostShare(period, costUsd) {
37654
+ const totalCost = period.totals.costUsd ?? 0;
37655
+ if (totalCost <= 0 || costUsd === void 0) {
37656
+ return void 0;
37657
+ }
37658
+ return `${period.totals.costIncomplete ? "~" : ""}${String(Math.round(costUsd / totalCost * 100))}%`;
37659
+ }
37285
37660
  function formatTopSource(period) {
37286
37661
  if (period.sources.length === 0) {
37287
37662
  return "-";
37288
37663
  }
37289
37664
  const topSource = period.sources[0];
37290
- const totalCost = period.totals.costUsd ?? 0;
37291
- if (totalCost <= 0 || topSource.costUsd === void 0) {
37292
- return topSource.source;
37293
- }
37294
- const share = `${String(Math.round(topSource.costUsd / totalCost * 100))}%`;
37295
- return `${topSource.source} (${period.totals.costIncomplete ? "~" : ""}${share})`;
37665
+ const share = formatCostShare(period, topSource.costUsd);
37666
+ return share === void 0 ? topSource.source : `${topSource.source} (${share})`;
37296
37667
  }
37297
37668
  function toTableRow(period) {
37298
37669
  return [
@@ -37357,12 +37728,38 @@ function renderTerminalActivity(activity, options, useColor) {
37357
37728
  ...heatmap.length > 0 ? ["", ...heatmap] : []
37358
37729
  ];
37359
37730
  }
37731
+ function formatTokenShare(tokens, totalTokens) {
37732
+ return `${String(Math.round(tokens / totalTokens * 100))}%`;
37733
+ }
37734
+ function formatMachinesNote2(monthToDate) {
37735
+ if (monthToDate?.machines === void 0 || monthToDate.machines.length < 2) {
37736
+ return void 0;
37737
+ }
37738
+ const byCost = (monthToDate.totals.costUsd ?? 0) > 0;
37739
+ if (!byCost && monthToDate.totals.totalTokens === 0) {
37740
+ return void 0;
37741
+ }
37742
+ const parts = monthToDate.machines.map((machine) => {
37743
+ if (!byCost) {
37744
+ const share2 = formatTokenShare(machine.totalTokens, monthToDate.totals.totalTokens);
37745
+ return `${machine.machine} ${formatCompact(machine.totalTokens)} tokens (${share2})`;
37746
+ }
37747
+ const cost = formatApproxUsd(machine.costUsd, machine.costIncomplete);
37748
+ const share = formatCostShare(monthToDate, machine.costUsd);
37749
+ return share === void 0 ? `${machine.machine} ${cost}` : `${machine.machine} ${cost} (${share})`;
37750
+ });
37751
+ return `This month by machine: ${parts.join(", ")}`;
37752
+ }
37360
37753
  function toMonthNotes(summaryData) {
37361
37754
  const { monthEnd } = summaryData;
37362
37755
  const monthToDate = summaryData.periods.find((period) => period.key === "monthToDate");
37363
37756
  const spentUsd = monthToDate?.totals.costUsd;
37364
37757
  const spent = formatApproxUsd(spentUsd, monthToDate?.totals.costIncomplete);
37365
37758
  const notes = [];
37759
+ const machinesNote = formatMachinesNote2(monthToDate);
37760
+ if (machinesNote !== void 0) {
37761
+ notes.push({ text: machinesNote, warning: false });
37762
+ }
37366
37763
  if (monthEnd.projectedCostUsd !== void 0) {
37367
37764
  notes.push({
37368
37765
  text: `On pace for ${formatApproxUsd(monthEnd.projectedCostUsd, monthEnd.costIncomplete)} this month`,
@@ -37544,6 +37941,80 @@ function renderStats(data, theme) {
37544
37941
  })
37545
37942
  ].join("\n");
37546
37943
  }
37944
+ var machinesLeft = right2 - 660;
37945
+ var machinesTop = 270;
37946
+ var machineSlotWidth = 190;
37947
+ var machineSlots = 3;
37948
+ function toMachineShares(machines, byCost, theme) {
37949
+ const totalCost = machines.reduce((sum, machine) => sum + (machine.costUsd ?? 0), 0);
37950
+ const totalTokens = machines.reduce((sum, machine) => sum + machine.totalTokens, 0);
37951
+ const shareOf = (machine) => byCost ? (machine.costUsd ?? 0) / totalCost : machine.totalTokens / totalTokens;
37952
+ const fills = [theme.heat[3], theme.heat[2], theme.textMuted];
37953
+ const named = machines.length <= machineSlots ? machines : machines.slice(0, machineSlots - 1);
37954
+ const shares = named.map((machine, index) => ({
37955
+ label: machine.machine,
37956
+ share: shareOf(machine),
37957
+ fill: fills[index]
37958
+ }));
37959
+ if (named.length < machines.length) {
37960
+ const rest = machines.slice(named.length);
37961
+ shares.push({
37962
+ label: `${formatInteger2(rest.length)} others`,
37963
+ share: rest.reduce((sum, machine) => sum + shareOf(machine), 0),
37964
+ fill: fills[machineSlots - 1]
37965
+ });
37966
+ }
37967
+ return shares;
37968
+ }
37969
+ function renderMachines(data, theme) {
37970
+ const monthToDate = findPeriod(data, "monthToDate");
37971
+ const machines = monthToDate?.machines ?? [];
37972
+ const byCost = machines.some((machine) => (machine.costUsd ?? 0) > 0);
37973
+ if (machines.length < 2 || !byCost && !machines.some((machine) => machine.totalTokens > 0)) {
37974
+ return "";
37975
+ }
37976
+ const shares = toMachineShares(machines, byCost, theme);
37977
+ const approximate = byCost && monthToDate?.totals.costIncomplete === true ? "~" : "";
37978
+ const barWidth = right2 - machinesLeft;
37979
+ const barTop = machinesTop + 12;
37980
+ const legendY = barTop + 30;
37981
+ let segmentX = machinesLeft;
37982
+ const parts = [
37983
+ svgText(
37984
+ machinesLeft,
37985
+ machinesTop,
37986
+ byCost ? "This month by machine" : "This month by machine (tokens)",
37987
+ {
37988
+ size: 16,
37989
+ fill: theme.textSecondary
37990
+ }
37991
+ ),
37992
+ `<rect x="${machinesLeft}" y="${barTop}" width="${barWidth}" height="8" rx="2" fill="${theme.line}"/>`
37993
+ ];
37994
+ shares.forEach((share, index) => {
37995
+ const width = share.share * barWidth;
37996
+ const slotX = machinesLeft + index * machineSlotWidth;
37997
+ const segmentLeft = segmentX;
37998
+ segmentX += width;
37999
+ parts.push(
38000
+ `<rect x="${segmentLeft.toFixed(2)}" y="${barTop}" width="${width.toFixed(2)}" height="8" rx="2" fill="${share.fill}"/>`,
38001
+ `<circle cx="${slotX + 5}" cy="${legendY - 5}" r="5" fill="${share.fill}"/>`,
38002
+ svgText(slotX + 18, legendY, truncateLabel(share.label, 12), {
38003
+ size: 15,
38004
+ fill: theme.text
38005
+ }),
38006
+ svgText(slotX + 166, legendY, `${approximate}${String(Math.round(share.share * 100))}%`, {
38007
+ size: 15,
38008
+ fill: theme.textSecondary,
38009
+ mono: true,
38010
+ anchor: "end"
38011
+ })
38012
+ );
38013
+ });
38014
+ return `<g data-machines="true">
38015
+ ${parts.join("\n")}
38016
+ </g>`;
38017
+ }
37547
38018
  function renderActivity(data, theme) {
37548
38019
  const { activity } = data;
37549
38020
  const gridBottom2 = gridTop2 + 7 * gridPitch;
@@ -37558,8 +38029,7 @@ function renderActivity(data, theme) {
37558
38029
  ].join("\n");
37559
38030
  }
37560
38031
  function renderSummaryShareSvg(data, theme) {
37561
- const body = data.activity.activeDays === 0 ? renderEmptyState(theme, "No usage in the past year") : `${renderStats(data, theme)}
37562
- ${renderActivity(data, theme)}`;
38032
+ const body = data.activity.activeDays === 0 ? renderEmptyState(theme, "No usage in the past year") : [renderStats(data, theme), renderMachines(data, theme), renderActivity(data, theme)].filter(Boolean).join("\n");
37563
38033
  return renderShareCard({
37564
38034
  theme,
37565
38035
  title: "LLM usage activity",
@@ -37740,15 +38210,31 @@ function isNarrowedByCliFilters(cliOptions) {
37740
38210
  const hasModel = Array.isArray(model) ? model.length > 0 : Boolean(model);
37741
38211
  return hasSource || hasModel || Boolean(cliOptions.provider);
37742
38212
  }
37743
- function compareSourcesByCost(left, right4) {
38213
+ function compareByCost(left, right4) {
37744
38214
  const costDelta = (right4.costUsd ?? 0) - (left.costUsd ?? 0);
37745
38215
  if (costDelta !== 0) {
37746
38216
  return costDelta;
37747
38217
  }
37748
- if (left.totalTokens !== right4.totalTokens) {
37749
- return right4.totalTokens - left.totalTokens;
38218
+ return right4.totalTokens - left.totalTokens;
38219
+ }
38220
+ function compareSourcesByCost(left, right4) {
38221
+ return compareByCost(left, right4) || compareByCodePoint(left.source, right4.source);
38222
+ }
38223
+ function compareMachinesByCost(left, right4) {
38224
+ return compareByCost(left, right4) || compareByCodePoint(left.machine, right4.machine);
38225
+ }
38226
+ function summarizeMachines(events, timezone, sourceOrder) {
38227
+ const eventsByMachine = /* @__PURE__ */ new Map();
38228
+ for (const event of events) {
38229
+ const machine = event.machine ?? LOCAL_MACHINE_NAME;
38230
+ const machineEvents = eventsByMachine.get(machine) ?? [];
38231
+ machineEvents.push(event);
38232
+ eventsByMachine.set(machine, machineEvents);
37750
38233
  }
37751
- return compareByCodePoint(left.source, right4.source);
38234
+ return [...eventsByMachine].map(([machine, machineEvents]) => ({
38235
+ machine,
38236
+ ...summarizeUsageWindow(machineEvents, timezone, sourceOrder).totals
38237
+ })).sort(compareMachinesByCost);
37752
38238
  }
37753
38239
  async function buildSummaryData(options, deps = {}) {
37754
38240
  const userConfigResolution = await resolveUserConfigForOptions(options, deps);
@@ -37771,16 +38257,16 @@ async function buildSummaryData(options, deps = {}) {
37771
38257
  );
37772
38258
  const { pricedEvents, pricingOrigin, pricingWarning, pricingSource } = await applyPricingToUsageEventDataset(dataset, deps, "auto");
37773
38259
  const sourceOrder = dataset.adaptersToParse.map((adapter) => adapter.id);
38260
+ const countsOtherMachines = pricedEvents.some((event) => event.machine !== void 0);
37774
38261
  const periods = measureRuntimeProfileStageSync(
37775
38262
  deps.runtimeProfile,
37776
38263
  "summary.aggregate",
37777
38264
  () => windows.map((window) => {
37778
- const summary = summarizeUsageWindow(
37779
- pricedEvents.filter((event) => isEventWithinWindow(event, window, timezone)),
37780
- timezone,
37781
- sourceOrder
38265
+ const windowEvents = pricedEvents.filter(
38266
+ (event) => isEventWithinWindow(event, window, timezone)
37782
38267
  );
37783
- return {
38268
+ const summary = summarizeUsageWindow(windowEvents, timezone, sourceOrder);
38269
+ const period = {
37784
38270
  key: window.key,
37785
38271
  label: window.label,
37786
38272
  since: window.since,
@@ -37788,6 +38274,10 @@ async function buildSummaryData(options, deps = {}) {
37788
38274
  totals: summary.totals,
37789
38275
  sources: [...summary.sources].map(([source, totals]) => ({ source, ...totals })).sort(compareSourcesByCost)
37790
38276
  };
38277
+ if (countsOtherMachines) {
38278
+ period.machines = summarizeMachines(windowEvents, timezone, sourceOrder);
38279
+ }
38280
+ return period;
37791
38281
  })
37792
38282
  );
37793
38283
  const activity = measureRuntimeProfileStageSync(
@@ -37855,6 +38345,52 @@ async function runSummaryReport(options) {
37855
38345
 
37856
38346
  // src/cli/run-statusline.ts
37857
38347
  var SEPARATOR = " \xB7 ";
38348
+ var STDIN_WAIT_MS = 250;
38349
+ function toFiniteNonNegative(value) {
38350
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
38351
+ }
38352
+ function parseClaudeCodeSession(input) {
38353
+ let parsed;
38354
+ try {
38355
+ parsed = JSON.parse(input);
38356
+ } catch {
38357
+ return void 0;
38358
+ }
38359
+ const record = asRecord(parsed);
38360
+ if (typeof record?.session_id !== "string") {
38361
+ return void 0;
38362
+ }
38363
+ return {
38364
+ costUsd: toFiniteNonNegative(asRecord(record.cost)?.total_cost_usd),
38365
+ contextPercent: toFiniteNonNegative(asRecord(record.context_window)?.used_percentage)
38366
+ };
38367
+ }
38368
+ async function readPipedInput(stdin) {
38369
+ if (stdin.isTTY) {
38370
+ return "";
38371
+ }
38372
+ const chunks = [];
38373
+ return new Promise((resolve) => {
38374
+ const finish = (input) => {
38375
+ clearTimeout(timer);
38376
+ stdin.removeAllListeners("data");
38377
+ resolve(input);
38378
+ };
38379
+ const timer = setTimeout(() => {
38380
+ stdin.destroy();
38381
+ finish("");
38382
+ }, STDIN_WAIT_MS);
38383
+ stdin.on("data", (chunk) => {
38384
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
38385
+ });
38386
+ stdin.once("end", () => {
38387
+ finish(Buffer.concat(chunks).toString("utf8"));
38388
+ });
38389
+ stdin.once("error", () => {
38390
+ finish("");
38391
+ });
38392
+ });
38393
+ }
37858
38394
  function findPeriod2(summary, key) {
37859
38395
  return summary.periods.find((period) => period.key === key);
37860
38396
  }
@@ -37874,8 +38410,17 @@ function formatMonth(summary) {
37874
38410
  const overPace = Math.max(spentUsd, projectedCostUsd ?? 0) > budgetUsd;
37875
38411
  return `${formatPeriodAmount(monthToDate)}/${formatUsd3(budgetUsd)} this month${overPace ? " \u26A0" : ""}`;
37876
38412
  }
37877
- function formatStatusline(summary) {
37878
- const parts = [`${formatPeriodAmount(findPeriod2(summary, "today"))} today`];
38413
+ function formatSession(session) {
38414
+ return [
38415
+ ...session?.costUsd === void 0 ? [] : [`${formatUsd3(session.costUsd)} session`],
38416
+ ...session?.contextPercent === void 0 ? [] : [`${String(Math.round(session.contextPercent))}% context`]
38417
+ ];
38418
+ }
38419
+ function formatStatusline(summary, session) {
38420
+ const parts = [
38421
+ ...formatSession(session),
38422
+ `${formatPeriodAmount(findPeriod2(summary, "today"))} today`
38423
+ ];
37879
38424
  if (summary.activity.currentStreak > 0) {
37880
38425
  parts.push(`${summary.activity.currentStreak}d streak`);
37881
38426
  }
@@ -37888,11 +38433,13 @@ async function buildStatuslineSummary(options, deps) {
37888
38433
  }
37889
38434
  return buildSummaryData({ ...options, pricingOffline: true, sync: false }, deps);
37890
38435
  }
37891
- async function runStatusline(options) {
38436
+ async function runStatusline(options, deps = {}) {
37892
38437
  if (!options.verbose) {
37893
38438
  setLogLevel("silent");
37894
38439
  }
38440
+ const input = readPipedInput(deps.stdin ?? process.stdin);
37895
38441
  const summary = await buildStatuslineSummary(options, {});
38442
+ const session = parseClaudeCodeSession(await input);
37896
38443
  if (options.verbose) {
37897
38444
  emitReportRunDiagnostics(summary.diagnostics, {
37898
38445
  emitCommonDiagnostics: emitDiagnostics,
@@ -37901,7 +38448,7 @@ async function runStatusline(options) {
37901
38448
  getRuntimeProfile: (diagnostics) => diagnostics.runtimeProfile
37902
38449
  });
37903
38450
  }
37904
- console.log(formatStatusline(summary));
38451
+ console.log(formatStatusline(summary, session));
37905
38452
  }
37906
38453
 
37907
38454
  // src/render/render-trends-report.ts
@@ -38865,7 +39412,10 @@ function emphasizeMarkdownRow(row, cells) {
38865
39412
  function renderMarkdownTable(rows, options = {}) {
38866
39413
  const tableLayout = options.tableLayout ?? "compact";
38867
39414
  const columns = getVisibleUsageColumnIndexes(
38868
- new Set(options.compact ? compactHiddenUsageColumns : [])
39415
+ /* @__PURE__ */ new Set([
39416
+ ...options.compact ? compactHiddenUsageColumns : [],
39417
+ ...options.hideCost ? ["cost"] : []
39418
+ ])
38869
39419
  );
38870
39420
  const bodyRows = toUsageTableCells(rows, {
38871
39421
  layout: tableLayout,
@@ -38891,12 +39441,15 @@ function getReportTitle4(granularity) {
38891
39441
  function joinWithAnd(items) {
38892
39442
  return items.length <= 1 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1) ?? ""}`;
38893
39443
  }
38894
- function describeTableFit(fit, compact) {
39444
+ function describeTableFit(fit, compact, hideCost = false) {
38895
39445
  const changes = [];
38896
39446
  if (!compact && fit.tokenFormat === "abbreviated") {
38897
39447
  changes.push("abbreviated token counts");
38898
39448
  }
38899
- const impliedHiddenColumns = compact ? compactHiddenUsageColumns : [];
39449
+ const impliedHiddenColumns = [
39450
+ ...compact ? compactHiddenUsageColumns : [],
39451
+ ...hideCost ? ["cost"] : []
39452
+ ];
38900
39453
  const hiddenHeaders = fit.hiddenColumns.filter((column) => !impliedHiddenColumns.includes(column)).map((column) => getUsageTableHeader(column));
38901
39454
  if (hiddenHeaders.length > 0) {
38902
39455
  changes.push(`hid ${joinWithAnd(hiddenHeaders)}`);
@@ -38928,9 +39481,10 @@ function renderTerminalUsageReport(usageData, options) {
38928
39481
  useColor,
38929
39482
  tableLayout,
38930
39483
  compact: options.compact,
39484
+ hideCost: options.hideCost,
38931
39485
  terminalWidth: options.terminalWidth
38932
39486
  });
38933
- const fitNote = describeTableFit(fit, options.compact ?? false);
39487
+ const fitNote = describeTableFit(fit, options.compact ?? false, options.hideCost);
38934
39488
  outputLines.push(output);
38935
39489
  return { output: outputLines.join("\n"), notes: fitNote ? [fitNote] : [] };
38936
39490
  }
@@ -38941,7 +39495,11 @@ function renderUsageReportWithNotes(usageData, format, options) {
38941
39495
  return { output: renderReportJson("usage", usageData.rows), notes: [] };
38942
39496
  case "markdown":
38943
39497
  return {
38944
- output: renderMarkdownTable(usageData.rows, { tableLayout, compact: options.compact }),
39498
+ output: renderMarkdownTable(usageData.rows, {
39499
+ tableLayout,
39500
+ compact: options.compact,
39501
+ hideCost: options.hideCost
39502
+ }),
38945
39503
  notes: []
38946
39504
  };
38947
39505
  case "terminal":
@@ -39064,7 +39622,7 @@ function renderPeriodLabels(periods, granularity, theme) {
39064
39622
  );
39065
39623
  }).join("\n");
39066
39624
  }
39067
- function renderUsageShareSvg(usageData, granularity, theme) {
39625
+ function renderUsageShareSvg(usageData, granularity, theme, options = {}) {
39068
39626
  const sourceRows = extractPeriodSourceRows(usageData.rows);
39069
39627
  const grandTotal = extractGrandTotal(usageData.rows);
39070
39628
  const usedPeriods = [...new Set(sourceRows.map((r) => r.periodKey))].sort(compareByCodePoint);
@@ -39076,7 +39634,19 @@ function renderUsageShareSvg(usageData, granularity, theme) {
39076
39634
  (_, index) => series.reduce((sum, entry) => sum + entry.values[index], 0)
39077
39635
  );
39078
39636
  const maxY = Math.max(1, ...periodTotals) * 1.08;
39079
- const body = series.length === 0 ? renderEmptyState(theme, "No usage in this window") : [
39637
+ const tokensDetail = `${formatInteger2(usedPeriods.length)} ${granularity === "daily" ? "days" : granularity === "weekly" ? "weeks" : "months"} with usage`;
39638
+ const stats = options.hideCost ? [
39639
+ renderStat({
39640
+ theme,
39641
+ x: SHARE_MARGIN,
39642
+ y: statsTop5,
39643
+ label: "Tokens",
39644
+ value: formatCompact(totalTokens),
39645
+ detail: tokensDetail,
39646
+ size: 44,
39647
+ accent: true
39648
+ })
39649
+ ] : [
39080
39650
  renderStat({
39081
39651
  theme,
39082
39652
  x: SHARE_MARGIN,
@@ -39092,9 +39662,12 @@ function renderUsageShareSvg(usageData, granularity, theme) {
39092
39662
  y: statsTop5 + 108,
39093
39663
  label: "Tokens",
39094
39664
  value: formatCompact(totalTokens),
39095
- detail: `${formatInteger2(usedPeriods.length)} ${granularity === "daily" ? "days" : granularity === "weekly" ? "weeks" : "months"} with usage`,
39665
+ detail: tokensDetail,
39096
39666
  size: 30
39097
- }),
39667
+ })
39668
+ ];
39669
+ const body = series.length === 0 ? renderEmptyState(theme, "No usage in this window") : [
39670
+ ...stats,
39098
39671
  renderLegend(series, totalTokens, theme),
39099
39672
  renderValueGridLines(
39100
39673
  maxY,
@@ -39134,6 +39707,7 @@ function resolveShareFileName(granularity) {
39134
39707
  }
39135
39708
  async function prepareUsageReport(granularity, options, deps = {}) {
39136
39709
  const tableLayout = resolveTableLayout(options);
39710
+ const hideCost = options.cost === false;
39137
39711
  return prepareReport({
39138
39712
  commandOptions: options,
39139
39713
  supportedFormats: usageReportFormats,
@@ -39144,12 +39718,15 @@ async function prepareUsageReport(granularity, options, deps = {}) {
39144
39718
  fileName: resolveShareFileName(granularity),
39145
39719
  logLabel: "usage",
39146
39720
  title: "Usage share card",
39147
- render: (theme) => renderUsageShareSvg(usageData, granularity, theme)
39721
+ render: (theme) => renderUsageShareSvg(usageData, granularity, theme, { hideCost })
39148
39722
  }) : void 0,
39149
39723
  validate: () => {
39150
39724
  if (options.compact && options.json) {
39151
39725
  throw new Error("--compact applies to terminal and markdown tables; drop it with --json");
39152
39726
  }
39727
+ if (hideCost && options.json) {
39728
+ throw new Error("--no-cost applies to tables and the share card; drop it with --json");
39729
+ }
39153
39730
  if (options.byMachine && options.share) {
39154
39731
  throw new Error("--by-machine splits the table only; drop it with --share");
39155
39732
  }
@@ -39158,7 +39735,8 @@ async function prepareUsageReport(granularity, options, deps = {}) {
39158
39735
  const { output, notes } = renderUsageReportWithNotes(usageData, format, {
39159
39736
  granularity,
39160
39737
  tableLayout,
39161
- compact: options.compact
39738
+ compact: options.compact,
39739
+ hideCost
39162
39740
  });
39163
39741
  return { output, hintsAfterOutput: notes };
39164
39742
  },
@@ -39483,6 +40061,7 @@ async function runDoctorReport(options, deps = {}) {
39483
40061
  }
39484
40062
 
39485
40063
  // src/cli/run-prune-report.ts
40064
+ import { statSync } from "fs";
39486
40065
  import { stat as stat8 } from "fs/promises";
39487
40066
 
39488
40067
  // src/render/render-prune-report.ts
@@ -39576,12 +40155,48 @@ async function discoverLiveFiles(adapters) {
39576
40155
  discoveredFiles.push(
39577
40156
  ...files.map((filePath) => ({
39578
40157
  source: adapter.id,
39579
- filePath
40158
+ filePath,
40159
+ adapter
39580
40160
  }))
39581
40161
  );
39582
40162
  }
39583
40163
  return { selectedSources, discoveredFiles };
39584
40164
  }
40165
+ async function readParseFingerprint(file) {
40166
+ try {
40167
+ return await getParseFileFingerprint(file.adapter, file.filePath);
40168
+ } catch {
40169
+ return void 0;
40170
+ }
40171
+ }
40172
+ function isDependencyUnchanged(dependency) {
40173
+ let now;
40174
+ try {
40175
+ const fileStat = statSync(dependency.path);
40176
+ now = { path: dependency.path, exists: true, size: fileStat.size, mtimeMs: fileStat.mtimeMs };
40177
+ } catch (error) {
40178
+ if (!hasErrorCode(error, "ENOENT")) {
40179
+ return false;
40180
+ }
40181
+ now = { path: dependency.path, exists: false };
40182
+ }
40183
+ return JSON.stringify(now) === JSON.stringify(dependency);
40184
+ }
40185
+ function splitLiveFilesByCurrentEntry(store, files) {
40186
+ const storedFingerprints = new Map(
40187
+ listStoredFileFingerprints(store).map((entry) => [
40188
+ JSON.stringify([entry.source, entry.filePath]),
40189
+ entry.fingerprint
40190
+ ])
40191
+ );
40192
+ const counted = [];
40193
+ const uncounted = [];
40194
+ for (const { file, fingerprint } of files) {
40195
+ const isCurrent = fingerprint !== void 0 && storedFingerprints.get(JSON.stringify([file.source, file.filePath])) === serializeEventStoreFingerprint(fingerprint) && fingerprint.dependencies.every(isDependencyUnchanged);
40196
+ (isCurrent ? counted : uncounted).push({ source: file.source, filePath: file.filePath });
40197
+ }
40198
+ return { counted, uncounted };
40199
+ }
39585
40200
  function toCandidate(file, options) {
39586
40201
  const reasons = [];
39587
40202
  if (options.includeSuppressed && file.suppressed) {
@@ -39703,32 +40318,57 @@ async function buildPruneReport(options, deps = {}) {
39703
40318
  };
39704
40319
  }
39705
40320
  return withEventStore(storePath, deps, async (store) => {
39706
- const classifiedFiles = classifyDepartedFiles(
40321
+ const isStillMissing = (filePath) => {
40322
+ try {
40323
+ statSync(filePath);
40324
+ return false;
40325
+ } catch (error) {
40326
+ return hasErrorCode(error, "ENOENT", "ENOTDIR");
40327
+ }
40328
+ };
40329
+ const fingerprints = await mapWithConcurrency(discoveredFiles, 32, readParseFingerprint);
40330
+ const fingerprintedFiles = discoveredFiles.map((file, index) => ({
40331
+ file,
40332
+ fingerprint: fingerprints[index]
40333
+ }));
40334
+ const { presentFiles: undiscoveredPresentFiles = [] } = await addStoredFilesStillOnDisk(
39707
40335
  store,
39708
- await addStoredFilesStillOnDisk(
39709
- store,
39710
- { selectedSources, discoveredFiles },
39711
- { unverifiable: "treat-as-live", statFile }
39712
- )
40336
+ {
40337
+ selectedSources,
40338
+ discoveredFiles: discoveredFiles.map(({ source, filePath }) => ({ source, filePath }))
40339
+ },
40340
+ { unverifiable: "treat-as-live", statFile }
39713
40341
  );
39714
- const candidates = buildCandidates(classifiedFiles, {
39715
- includeSuppressed: Boolean(options.suppressed),
39716
- departedBeforeTimestamp
39717
- });
39718
- const candidateEventCount = countCandidateEvents(candidates);
40342
+ const selectCandidates = () => {
40343
+ const { counted, uncounted } = splitLiveFilesByCurrentEntry(store, fingerprintedFiles);
40344
+ return buildCandidates(
40345
+ classifyDepartedFiles(store, {
40346
+ selectedSources,
40347
+ discoveredFiles: counted,
40348
+ presentFiles: [...uncounted, ...undiscoveredPresentFiles],
40349
+ repeatingSources: getRepeatingSourceIds(adapters)
40350
+ }),
40351
+ { includeSuppressed: Boolean(options.suppressed), departedBeforeTimestamp }
40352
+ );
40353
+ };
39719
40354
  if (!options.apply) {
40355
+ const candidates2 = selectCandidates();
39720
40356
  return {
39721
- candidates,
40357
+ candidates: candidates2,
39722
40358
  summary: {
39723
40359
  storePath,
39724
40360
  applied: false,
39725
- candidateFileCount: candidates.length,
39726
- candidateEventCount
40361
+ candidateFileCount: candidates2.length,
40362
+ candidateEventCount: countCandidateEvents(candidates2)
39727
40363
  }
39728
40364
  };
39729
40365
  }
39730
40366
  const sizeBefore = await readStoreSizeSnapshot(storePath, statFile);
39731
- const deleteResult = deleteStoredFiles(store, candidates);
40367
+ const { candidates, deleteResult } = runTransaction(store.database, () => {
40368
+ const selected = selectCandidates().filter((candidate) => isStillMissing(candidate.filePath));
40369
+ return { candidates: selected, deleteResult: deleteStoredFiles(store, selected) };
40370
+ });
40371
+ const candidateEventCount = countCandidateEvents(candidates);
39732
40372
  vacuumEventStore(store);
39733
40373
  store.database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
39734
40374
  const sizeAfter = await readStoreSizeSnapshot(storePath, statFile);
@@ -40742,6 +41382,10 @@ function createUsageReportDefinition(granularity) {
40742
41382
  {
40743
41383
  command: "llm-usage weekly --all --json",
40744
41384
  includeInCliReference: true
41385
+ },
41386
+ {
41387
+ command: "llm-usage weekly --last 1 --no-cost",
41388
+ includeInCliReference: true
40745
41389
  }
40746
41390
  ],
40747
41391
  monthly: [
@@ -40777,12 +41421,17 @@ function createUsageReportDefinition(granularity) {
40777
41421
  {
40778
41422
  command: "llm-usage monthly --share",
40779
41423
  includeInCliReference: true
41424
+ },
41425
+ {
41426
+ command: "llm-usage monthly --last 3",
41427
+ includeInRootHelp: true,
41428
+ includeInCliReference: true
40780
41429
  }
40781
41430
  ]
40782
41431
  };
40783
41432
  const descriptionByGranularity = {
40784
- daily: "Show daily usage report (last 7 days unless --since, --until, or --all)",
40785
- weekly: "Show weekly usage report (week starts Monday; last 8 weeks unless --since, --until, or --all)",
41433
+ daily: "Show daily usage report (last 7 days unless --last, --since, --until, or --all)",
41434
+ weekly: "Show weekly usage report (week starts Monday; last 8 weeks unless --last, --since, --until, or --all)",
40786
41435
  monthly: "Show monthly usage report"
40787
41436
  };
40788
41437
  return {
@@ -40797,16 +41446,17 @@ function createUsageReportDefinition(granularity) {
40797
41446
  register(command) {
40798
41447
  const defaultWindow = DEFAULT_REPORT_WINDOWS[granularity];
40799
41448
  if (defaultWindow) {
40800
- const { period, periods } = defaultWindow;
41449
+ const { period: period2, periods } = defaultWindow;
40801
41450
  command.option(
40802
41451
  "--all",
40803
- `Report every ${period} instead of the last ${String(periods)} ${period}s`
41452
+ `Report every ${period2} instead of the last ${String(periods)} ${period2}s`
40804
41453
  );
40805
41454
  }
40806
- command.option(
41455
+ const period = getReportPeriodName(granularity);
41456
+ command.option("--last <n>", `Report the last N ${period}s, counting the current ${period}`).option(
40807
41457
  "--compact",
40808
41458
  "Abbreviate token counts and hide the Reasoning and Cache Write columns (terminal/markdown)"
40809
- ).option("--by-machine", "Split each source row by the machine its usage came from");
41459
+ ).option("--no-cost", "Leave cost out of the table and share card, e.g. for screenshots").option("--by-machine", "Split each source row by the machine its usage came from");
40810
41460
  command.action((options) => runUsageReport(granularity, options));
40811
41461
  return command;
40812
41462
  }
@@ -41099,7 +41749,7 @@ var statuslineDefinition = {
41099
41749
  commandName: "statusline",
41100
41750
  docsLabel: "statusline",
41101
41751
  kind: "specialized",
41102
- description: "Print one line with today's cost, your streak, and month to date (for status bars; never fetches)",
41752
+ description: "Print one line with today's cost, your streak, and month to date, led by the session's cost and context use under Claude Code (for status bars; never fetches)",
41103
41753
  sharedOptionProfile: "statusline",
41104
41754
  helpExamples: [
41105
41755
  {
@@ -41348,7 +41998,10 @@ async function runMachineExport(options, deps = {}) {
41348
41998
  const store = await (deps.openEventStore ?? openEventStore)(dataset.ledger.path);
41349
41999
  let snapshots;
41350
42000
  try {
41351
- snapshots = readStoredFileSnapshots(store, dataset.ledger.countedFiles);
42001
+ snapshots = readStoredFileSnapshots(store, dataset.ledger.parsedFiles, {
42002
+ historyFiles: dataset.ledger.historyFiles,
42003
+ repeatingSources: new Set(getRepeatingSourceIds(dataset.adaptersToParse))
42004
+ });
41352
42005
  } finally {
41353
42006
  (deps.closeEventStore ?? closeEventStore)(store);
41354
42007
  }
@@ -41376,8 +42029,9 @@ async function runMachineExport(options, deps = {}) {
41376
42029
  // src/machines/machine-config-file.ts
41377
42030
  import { isDeepStrictEqual } from "util";
41378
42031
  import { randomUUID as randomUUID3 } from "crypto";
41379
- import { readFile as readFile8, realpath as realpath4, rename as rename2, rm as rm4, stat as stat9, writeFile as writeFile5 } from "fs/promises";
42032
+ import { open as open3, readFile as readFile8, realpath as realpath4, rename as rename2, rm as rm4, stat as stat9, writeFile as writeFile5 } from "fs/promises";
41380
42033
  import path40 from "path";
42034
+ import { setTimeout as sleep4 } from "timers/promises";
41381
42035
  async function readConfigText(configPath) {
41382
42036
  try {
41383
42037
  return await readFile8(configPath, "utf8");
@@ -41400,9 +42054,40 @@ function withoutMachine(config, name) {
41400
42054
  delete rest.machines;
41401
42055
  return Object.keys(machines).length === 0 ? rest : { ...rest, machines };
41402
42056
  }
41403
- async function replaceConfigFile(configPath, content) {
42057
+ var CONFIG_LOCK_RETRY_MS = 50;
42058
+ var CONFIG_LOCK_TIMEOUT_MS = 5e3;
42059
+ async function resolveConfigTarget(configPath) {
42060
+ return realpath4(configPath).catch(() => configPath);
42061
+ }
42062
+ async function withConfigLock(configPath, edit) {
41404
42063
  await ensureDirectory(path40.dirname(configPath));
41405
- const targetPath = await realpath4(configPath).catch(() => configPath);
42064
+ const lockPath = `${await resolveConfigTarget(configPath)}.lock`;
42065
+ const deadline = Date.now() + CONFIG_LOCK_TIMEOUT_MS;
42066
+ for (; ; ) {
42067
+ try {
42068
+ await (await open3(lockPath, "wx")).close();
42069
+ break;
42070
+ } catch (error) {
42071
+ if (!hasErrorCode(error, "EEXIST")) {
42072
+ throw error;
42073
+ }
42074
+ if (Date.now() > deadline) {
42075
+ throw new Error(
42076
+ `Another llm-usage run is editing ${configPath}; try again, or delete ${lockPath} if no other run is (one that crashed leaves it behind)`,
42077
+ { cause: error }
42078
+ );
42079
+ }
42080
+ await sleep4(CONFIG_LOCK_RETRY_MS);
42081
+ }
42082
+ }
42083
+ try {
42084
+ return await edit();
42085
+ } finally {
42086
+ await rm4(lockPath, { force: true });
42087
+ }
42088
+ }
42089
+ async function replaceConfigFile(configPath, content) {
42090
+ const targetPath = await resolveConfigTarget(configPath);
41406
42091
  const mode = (await stat9(targetPath).catch(() => void 0))?.mode;
41407
42092
  const temporaryPath = `${targetPath}.${randomUUID3()}.tmp`;
41408
42093
  try {
@@ -41428,6 +42113,9 @@ function formatMachineBlock(name, machine, eol = "\n") {
41428
42113
  return `${lines.join(eol)}${eol}`;
41429
42114
  }
41430
42115
  async function addMachineToConfigFile(configPath, name, machine) {
42116
+ await withConfigLock(configPath, () => addMachineUnderLock(configPath, name, machine));
42117
+ }
42118
+ async function addMachineUnderLock(configPath, name, machine) {
41431
42119
  const current = await readConfigText(configPath) ?? "";
41432
42120
  const eol = current.includes("\r\n") ? "\r\n" : "\n";
41433
42121
  const separator = current.length === 0 ? "" : current.endsWith("\n") ? eol : `${eol}${eol}`;
@@ -41454,6 +42142,9 @@ function isMachineHeader(line, name) {
41454
42142
  return header === `[machines.${name}]` || header === `[machines."${name}"]`;
41455
42143
  }
41456
42144
  async function removeMachineFromConfigFile(configPath, name) {
42145
+ await withConfigLock(configPath, () => removeMachineUnderLock(configPath, name));
42146
+ }
42147
+ async function removeMachineUnderLock(configPath, name) {
41457
42148
  const current = await readConfigText(configPath) ?? "";
41458
42149
  const lines = current.split("\n");
41459
42150
  const start = lines.findIndex((line) => isMachineHeader(line, name));
@@ -42196,6 +42887,11 @@ var events_line_v1_schema_default = {
42196
42887
  description: "Normalized model identifier; omitted when unknown.",
42197
42888
  type: "string"
42198
42889
  },
42890
+ cacheWrite1hTokens: {
42891
+ description: "The part of cacheWriteTokens cached for one hour, which bills at a higher rate (Claude). Absent from events written by versions before 0.11.",
42892
+ type: "integer",
42893
+ minimum: 0
42894
+ },
42199
42895
  costUsd: {
42200
42896
  description: "Cost in USD; omitted when pricing is unresolved.",
42201
42897
  type: "number",
@@ -43243,6 +43939,33 @@ var report_summary_v1_schema_default = {
43243
43939
  required: ["source", "events", "activeDays"],
43244
43940
  unevaluatedProperties: false
43245
43941
  },
43942
+ machineTotals: {
43943
+ type: "object",
43944
+ description: "Usage totals for one machine within a period (non-negative).",
43945
+ allOf: [
43946
+ {
43947
+ $ref: "https://ayagmar.github.io/llm-usage-metrics/report-common.v1.schema.json#/$defs/usageTotals"
43948
+ }
43949
+ ],
43950
+ properties: {
43951
+ machine: {
43952
+ description: "`local` for the machine that ran the report, otherwise the name the machine was added under.",
43953
+ type: "string"
43954
+ },
43955
+ events: {
43956
+ description: "Events in the period.",
43957
+ type: "integer",
43958
+ minimum: 0
43959
+ },
43960
+ activeDays: {
43961
+ description: "Days with at least one event in the period.",
43962
+ type: "integer",
43963
+ minimum: 0
43964
+ }
43965
+ },
43966
+ required: ["machine", "events", "activeDays"],
43967
+ unevaluatedProperties: false
43968
+ },
43246
43969
  period: {
43247
43970
  description: "One summarized period.",
43248
43971
  type: "object",
@@ -43272,6 +43995,13 @@ var report_summary_v1_schema_default = {
43272
43995
  items: {
43273
43996
  $ref: "#/$defs/sourceTotals"
43274
43997
  }
43998
+ },
43999
+ machines: {
44000
+ description: "Machines with usage in the period, by cost then tokens then name. Present only when the report counts other machines' usage in the past year, after its source, provider and model filters (since 0.11).",
44001
+ type: "array",
44002
+ items: {
44003
+ $ref: "#/$defs/machineTotals"
44004
+ }
43275
44005
  }
43276
44006
  },
43277
44007
  required: ["key", "label", "since", "until", "totals", "sources"],