tokentracker-cli 0.84.5 → 0.84.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/{AchievementBadge-DJvRfvu1.js → AchievementBadge-CKIvD3lG.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-Cr8WToxS.js → AchievementsPage-CZWCUiS5.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-BCPYB46e.js → AchievementsSection-BSk_fR8P.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-D-12Ek-H.js → BadgeDetailModal-CjHkcmuq.js} +1 -1
- package/dashboard/dist/assets/{Card-Ha3x9zIj.js → Card-D6hdzwvI.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-DoGgAppT.js → ClawdAnimated-Cl6gmF7u.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-DIKocEAD.js → CommandPalette-B0rcodcF.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BNuMbEWy.js → CommunityStatsModal-BtlTJbf4.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-F2gPIIS9.js → ConfirmModal-tJCWVZQH.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-YvoCEGG9.js → DashboardPage-Dtxq8dzI.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-CbC_J22s.js → DevicePage-9USuEsRu.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-VEynQCYg.js → DialogClose-DxHdqMcl.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-DgUSe6A1.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BbAVHI_v.js → DialogTitle-C1ZeQgf2.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CZVqSnis.js → FadeIn-Cx4jLl9G.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Cs_h332Y.js → HeaderGithubStar-BOyuGkoA.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DvvHTphK.js → HoverTooltip-BDhqMR3L.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-DPVmqSS_.js → IpCheckPage-DTvA9Wmx.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-BCvNh3zE.js → LandingPage-k1271lWV.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-BAPZ2ROX.js → LeaderboardAvatar-3ibNg6If.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-DZSxazHP.js → LeaderboardPage-Byww5UpT.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-r03akfe2.js → LeaderboardProfileModal-DeGbJtvi.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CU63YW2M.js → LeaderboardProfilePage-DEPzO6hW.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-JWZDa5_Y.js → LimitsPage-DPtqL0PS.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-BHlcPCZk.js → LocalOnlyNotice-C76LAT_3.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-C-dqUumR.js → LoginCard-DEyCJT67.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-BFzRg4Iy.js → LoginModal-CueMQgAZ.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-QtlnGF1S.js → LoginPage-CwqBb5Fd.js} +1 -1
- package/dashboard/dist/assets/{PetPage-C6UMY2qD.js → PetPage--xQAjW4_.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-CltmqcrT.js → PopoverPopup-Dt9VY2VQ.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-BqOI9EiD.js → ProviderIcon-CHx4ohfx.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BsfCBUZD.js → ResetPasswordPage-DB6R7fjf.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-D4biXw_Q.js → SegmentedControl-BINOpXIY.js} +1 -1
- package/dashboard/dist/assets/{Select-DTw-14iE.js → Select-DcYXcsKv.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-3tUrnu3f.js → SelectItemText-BDv2Kb3x.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-DYYlNqHg.js → SessionsPage-cL_w0ytB.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-CYlEboSl.js → SettingsPage-CHGA0bQS.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-Bzq8V5Pd.js → SkillsPage-I29SqkXO.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-DU77e8sM.js → TokenGalaxy-BHq4YSv2.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-DV3KGf-a.js → ToolbarRootContext-akXaaKb6.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-D6QYKmbl.js → TrendMonitor-DPhFXzQc.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DSP4jCsH.js → WidgetsPage-Nf0UEz7s.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-B0x6O_DU.js → WrappedPage-D4zOSsqD.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-D3llGq6p.js → __vite-browser-external-UqlSqI-1.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-B2S_cdOL.js → agent-logos-Bja3VCAa.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DNUVuhx2.js → arrow-up-right-CzcCNJJ2.js} +1 -1
- package/dashboard/dist/assets/{check-C3ARiiE_.js → check-DUHSZCYn.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-3bY7H8jO.js → chevron-right-jLvhaNUy.js} +1 -1
- package/dashboard/dist/assets/{copy-C-UIMKoO.js → copy-ChyH-ynx.js} +1 -1
- package/dashboard/dist/assets/{download-DtRzJfcr.js → download-CgwvXrUe.js} +1 -1
- package/dashboard/dist/assets/{flame-Bas3ry-g.js → flame-ysf1F8uB.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DFtRUIH8.js → format-tokens-DAa2cubl.js} +1 -1
- package/dashboard/dist/assets/{icons-CHBt4Tmo.js → icons-DccoWQq9.js} +1 -1
- package/dashboard/dist/assets/{index-4unZlm2w.js → index-CAycNPQf.js} +1 -1
- package/dashboard/dist/assets/{index-C1ADnvzm.js → index-CxExExXh.js} +1 -1
- package/dashboard/dist/assets/{index-CZRlrK7w.js → index-kkQrDRat.js} +1 -1
- package/dashboard/dist/assets/{info-BkrO-fg3.js → info-DWQQIq9M.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-BtwYmYHA.js → limits-providers-B_18X9Rp.js} +1 -1
- package/dashboard/dist/assets/{link-2-DTLFz8uG.js → link-2-CKeVVq4P.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-BqQW_Ipq.js → loader-circle-Dw0tu6IN.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-YdpeE4dq.js → maximize-2-BMU0YzEF.js} +1 -1
- package/dashboard/dist/assets/{provider-display-CgBOpjq0.js → provider-display-Cf9I-VLS.js} +1 -1
- package/dashboard/dist/assets/{react-DD9jpv--.js → react-BhbcISKy.js} +1 -1
- package/dashboard/dist/assets/{search--TvX-fig.js → search-BoY8EW1z.js} +1 -1
- package/dashboard/dist/assets/{skills-api-C5C8a1at.js → skills-api-KVH21GJw.js} +1 -1
- package/dashboard/dist/assets/{store-CRA8gPnP.js → store-BL7rSajG.js} +1 -1
- package/dashboard/dist/assets/{terminal-BXZyl2ek.js → terminal-CeMuITdm.js} +1 -1
- package/dashboard/dist/assets/{trash-2-IFdBQ65v.js → trash-2-jVUCKqrR.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-LyJ5aMpX.js → use-community-stats-uWK0fE6l.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CKvFS1Nn.js → use-limits-display-prefs-DWsF4ShM.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-Cg-4j_0z.js → use-native-settings-SEbqWDvc.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-2pX5PXZO.js → use-ordered-list-D4UhrPvw.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-C3UrOh55.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-DilJOODA.js → use-session-efficiency-pref-DbysyUTJ.js} +1 -1
- package/dashboard/dist/assets/{use-transform-DiyYTLpI.js → use-transform-DaEssGaZ.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-DkUmp_nW.js → use-usage-limits-CmmQ34Ov.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-lm7KjGoD.js → useCurrency-DvBEn4rx.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-DVSyJ-GE.js → useScrollLock-QozsdFng.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-Li8BANKO.js → useTokenFormat-BTLrL7Mi.js} +1 -1
- package/dashboard/dist/assets/{zap-CKZt1_i5.js → zap-Dp_aJ1q6.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/status.js +17 -7
- package/src/commands/sync.js +333 -5
- package/src/lib/codex-rollout-parser.js +17 -39
- package/src/lib/codex-token-usage.js +195 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +359 -91
- package/src/lib/usage-limits.js +211 -41
- package/dashboard/dist/assets/DialogDescription-B9bb9Ar0.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-BCR01kRg.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -8,6 +8,11 @@ const { ensureDir } = require("./fs");
|
|
|
8
8
|
const { readSqliteJsonRows, readSqliteJsonRowsAsync } = require("./sqlite-reader");
|
|
9
9
|
const wsl = require("./wsl-probe");
|
|
10
10
|
const { resolveInstallPaths } = require("./install-resolver");
|
|
11
|
+
const {
|
|
12
|
+
consumeUsageDelta,
|
|
13
|
+
createUsageDeltaState,
|
|
14
|
+
snapshotUsageBaselines,
|
|
15
|
+
} = require("./codex-token-usage");
|
|
11
16
|
|
|
12
17
|
const DEFAULT_SOURCE = "codex";
|
|
13
18
|
const DEFAULT_MODEL = "unknown";
|
|
@@ -360,6 +365,9 @@ async function parseRolloutIncremental({
|
|
|
360
365
|
const lastTotal = sameInode && !truncated && !rebuildingCodexBaseline
|
|
361
366
|
? prev.lastTotal || null
|
|
362
367
|
: null;
|
|
368
|
+
const tokenUsageBaselines = sameInode && !truncated && !rebuildingCodexBaseline
|
|
369
|
+
? prev.tokenUsageBaselines || null
|
|
370
|
+
: null;
|
|
363
371
|
const lastModel = sameInode && !truncated ? prev.lastModel || null : null;
|
|
364
372
|
|
|
365
373
|
const codexProjectFastPath = projectEnabled && fileSource === DEFAULT_SOURCE;
|
|
@@ -430,6 +438,7 @@ async function parseRolloutIncremental({
|
|
|
430
438
|
filePath,
|
|
431
439
|
fileStat: st,
|
|
432
440
|
lastTotal,
|
|
441
|
+
tokenUsageBaselines,
|
|
433
442
|
lastModel,
|
|
434
443
|
projectState,
|
|
435
444
|
projectMetaCache,
|
|
@@ -442,6 +451,7 @@ async function parseRolloutIncremental({
|
|
|
442
451
|
fileStat: st,
|
|
443
452
|
startOffset,
|
|
444
453
|
lastTotal,
|
|
454
|
+
tokenUsageBaselines,
|
|
445
455
|
lastModel,
|
|
446
456
|
hourlyState,
|
|
447
457
|
touchedBuckets,
|
|
@@ -462,6 +472,7 @@ async function parseRolloutIncremental({
|
|
|
462
472
|
inode,
|
|
463
473
|
offset: result.endOffset,
|
|
464
474
|
lastTotal: result.lastTotal,
|
|
475
|
+
tokenUsageBaselines: result.tokenUsageBaselines,
|
|
465
476
|
lastModel: result.lastModel,
|
|
466
477
|
updatedAt: new Date().toISOString(),
|
|
467
478
|
};
|
|
@@ -1731,6 +1742,7 @@ async function parseRolloutFile({
|
|
|
1731
1742
|
fileStat,
|
|
1732
1743
|
startOffset,
|
|
1733
1744
|
lastTotal,
|
|
1745
|
+
tokenUsageBaselines,
|
|
1734
1746
|
lastModel,
|
|
1735
1747
|
hourlyState,
|
|
1736
1748
|
touchedBuckets,
|
|
@@ -1751,14 +1763,25 @@ async function parseRolloutFile({
|
|
|
1751
1763
|
const projectFileContexts = [];
|
|
1752
1764
|
addProjectFileContext(projectFileContexts, projectContext);
|
|
1753
1765
|
if (startOffset >= endOffset) {
|
|
1754
|
-
return {
|
|
1766
|
+
return {
|
|
1767
|
+
endOffset,
|
|
1768
|
+
lastTotal,
|
|
1769
|
+
tokenUsageBaselines,
|
|
1770
|
+
lastModel,
|
|
1771
|
+
eventsAggregated: 0,
|
|
1772
|
+
projectFileContexts,
|
|
1773
|
+
};
|
|
1755
1774
|
}
|
|
1756
1775
|
|
|
1757
1776
|
const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: startOffset });
|
|
1758
1777
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1759
1778
|
|
|
1760
1779
|
let model = typeof lastModel === "string" ? lastModel : null;
|
|
1761
|
-
|
|
1780
|
+
const usageDeltaState = createUsageDeltaState({
|
|
1781
|
+
lastTotal,
|
|
1782
|
+
baselines: tokenUsageBaselines,
|
|
1783
|
+
});
|
|
1784
|
+
let latestTotal = lastTotal && typeof lastTotal === "object" ? lastTotal : null;
|
|
1762
1785
|
let currentCwd = null;
|
|
1763
1786
|
let currentDate = null;
|
|
1764
1787
|
let isForkedRollout = false;
|
|
@@ -1840,15 +1863,13 @@ async function parseRolloutFile({
|
|
|
1840
1863
|
|
|
1841
1864
|
const lastUsage = info.last_token_usage;
|
|
1842
1865
|
const totalUsage = info.total_token_usage;
|
|
1866
|
+
if (totalUsage && typeof totalUsage === "object") latestTotal = totalUsage;
|
|
1843
1867
|
|
|
1844
|
-
const
|
|
1845
|
-
|
|
1868
|
+
const rawDelta = consumeUsageDelta(usageDeltaState, lastUsage, totalUsage);
|
|
1869
|
+
const delta = rawDelta ? normalizeUsage(rawDelta) : null;
|
|
1870
|
+
if (!delta || isAllZeroUsage(delta)) continue;
|
|
1846
1871
|
delta.conversation_count = 1;
|
|
1847
1872
|
|
|
1848
|
-
if (totalUsage && typeof totalUsage === "object") {
|
|
1849
|
-
totals = totalUsage;
|
|
1850
|
-
}
|
|
1851
|
-
|
|
1852
1873
|
// Forked Codex rollouts replay the parent session's entire token history
|
|
1853
1874
|
// into the child file the moment the fork is created. The date guard below
|
|
1854
1875
|
// (current_date < rolloutDate) catches cross-day forks, but same-day forks
|
|
@@ -1863,8 +1884,8 @@ async function parseRolloutFile({
|
|
|
1863
1884
|
// flush spacing and well below genuine turn cadence (≥~4.6s observed). The
|
|
1864
1885
|
// first replayed row cannot be identified without lookahead, so it is still
|
|
1865
1886
|
// counted (a bounded, <1% residual over-count); dropping real usage is the
|
|
1866
|
-
// worse failure, so we bias against it. `
|
|
1867
|
-
// keeping the cumulative
|
|
1887
|
+
// worse failure, so we bias against it. `usageDeltaState` is already advanced above,
|
|
1888
|
+
// keeping the cumulative lineage correct for the live turns we keep.
|
|
1868
1889
|
// Scoped to forked codex rollouts. (issue #169 follow-up.)
|
|
1869
1890
|
let forkedReplaySkip = false;
|
|
1870
1891
|
if (isForkedRollout && source === DEFAULT_SOURCE && replayPrefixActive) {
|
|
@@ -1903,8 +1924,8 @@ async function parseRolloutFile({
|
|
|
1903
1924
|
// buckets. External tools rewrite session files without changing the token
|
|
1904
1925
|
// data — Codex-Manager atomically rewrites them (new inode) to patch the
|
|
1905
1926
|
// provider on every account/channel switch — so without dedup each switch
|
|
1906
|
-
// double-counts the rewritten sessions. `
|
|
1907
|
-
// so skipping an already-seen event keeps the cumulative
|
|
1927
|
+
// double-counts the rewritten sessions. `usageDeltaState` is already advanced above,
|
|
1928
|
+
// so skipping an already-seen event keeps the cumulative lineage intact
|
|
1908
1929
|
// while preventing the re-add; genuinely new turns carry new timestamps and
|
|
1909
1930
|
// are still counted. Key = sessionUUID:eventTimestamp (both stable across the
|
|
1910
1931
|
// rewrite and across a sessions/ -> archived_sessions/ move).
|
|
@@ -1939,13 +1960,21 @@ async function parseRolloutFile({
|
|
|
1939
1960
|
eventsAggregated += 1;
|
|
1940
1961
|
}
|
|
1941
1962
|
|
|
1942
|
-
return {
|
|
1963
|
+
return {
|
|
1964
|
+
endOffset,
|
|
1965
|
+
lastTotal: latestTotal,
|
|
1966
|
+
tokenUsageBaselines: snapshotUsageBaselines(usageDeltaState),
|
|
1967
|
+
lastModel: model,
|
|
1968
|
+
eventsAggregated,
|
|
1969
|
+
projectFileContexts,
|
|
1970
|
+
};
|
|
1943
1971
|
}
|
|
1944
1972
|
|
|
1945
1973
|
async function scanRolloutProjectFileContexts({
|
|
1946
1974
|
filePath,
|
|
1947
1975
|
fileStat,
|
|
1948
1976
|
lastTotal,
|
|
1977
|
+
tokenUsageBaselines,
|
|
1949
1978
|
lastModel,
|
|
1950
1979
|
projectState,
|
|
1951
1980
|
projectMetaCache,
|
|
@@ -1958,7 +1987,14 @@ async function scanRolloutProjectFileContexts({
|
|
|
1958
1987
|
const projectFileContexts = [];
|
|
1959
1988
|
addProjectFileContext(projectFileContexts, projectContext);
|
|
1960
1989
|
if (!projectState || endOffset <= 0) {
|
|
1961
|
-
return {
|
|
1990
|
+
return {
|
|
1991
|
+
endOffset,
|
|
1992
|
+
lastTotal,
|
|
1993
|
+
tokenUsageBaselines,
|
|
1994
|
+
lastModel,
|
|
1995
|
+
eventsAggregated: 0,
|
|
1996
|
+
projectFileContexts,
|
|
1997
|
+
};
|
|
1962
1998
|
}
|
|
1963
1999
|
|
|
1964
2000
|
const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: 0 });
|
|
@@ -2005,7 +2041,14 @@ async function scanRolloutProjectFileContexts({
|
|
|
2005
2041
|
addProjectFileContext(projectFileContexts, context);
|
|
2006
2042
|
}
|
|
2007
2043
|
|
|
2008
|
-
return {
|
|
2044
|
+
return {
|
|
2045
|
+
endOffset,
|
|
2046
|
+
lastTotal,
|
|
2047
|
+
tokenUsageBaselines,
|
|
2048
|
+
lastModel,
|
|
2049
|
+
eventsAggregated: 0,
|
|
2050
|
+
projectFileContexts,
|
|
2051
|
+
};
|
|
2009
2052
|
}
|
|
2010
2053
|
|
|
2011
2054
|
async function parseClaudeFile({
|
|
@@ -3570,48 +3613,6 @@ function extractTokenCount(obj) {
|
|
|
3570
3613
|
return null;
|
|
3571
3614
|
}
|
|
3572
3615
|
|
|
3573
|
-
function pickDelta(lastUsage, totalUsage, prevTotals) {
|
|
3574
|
-
const hasLast = isNonEmptyObject(lastUsage);
|
|
3575
|
-
const hasTotal = isNonEmptyObject(totalUsage);
|
|
3576
|
-
const hasPrevTotals = isNonEmptyObject(prevTotals);
|
|
3577
|
-
|
|
3578
|
-
if (hasTotal && hasPrevTotals) {
|
|
3579
|
-
if (totalsReset(totalUsage, prevTotals)) {
|
|
3580
|
-
const resetUsage = hasLast ? lastUsage : totalUsage;
|
|
3581
|
-
const normalized = normalizeUsage(resetUsage);
|
|
3582
|
-
return isAllZeroUsage(normalized) ? null : normalized;
|
|
3583
|
-
}
|
|
3584
|
-
|
|
3585
|
-
const delta = {};
|
|
3586
|
-
for (const k of [
|
|
3587
|
-
"input_tokens",
|
|
3588
|
-
"cached_input_tokens",
|
|
3589
|
-
"cache_creation_input_tokens",
|
|
3590
|
-
"output_tokens",
|
|
3591
|
-
"reasoning_output_tokens",
|
|
3592
|
-
"total_tokens",
|
|
3593
|
-
]) {
|
|
3594
|
-
const a = Number(totalUsage[k]);
|
|
3595
|
-
const b = Number(prevTotals[k]);
|
|
3596
|
-
if (Number.isFinite(a) && Number.isFinite(b)) delta[k] = Math.max(0, a - b);
|
|
3597
|
-
}
|
|
3598
|
-
const normalized = normalizeUsage(delta);
|
|
3599
|
-
return isAllZeroUsage(normalized) ? null : normalized;
|
|
3600
|
-
}
|
|
3601
|
-
|
|
3602
|
-
if (hasLast) {
|
|
3603
|
-
const normalized = normalizeUsage(lastUsage);
|
|
3604
|
-
return isAllZeroUsage(normalized) ? null : normalized;
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
if (hasTotal) {
|
|
3608
|
-
const normalized = normalizeUsage(totalUsage);
|
|
3609
|
-
return isAllZeroUsage(normalized) ? null : normalized;
|
|
3610
|
-
}
|
|
3611
|
-
|
|
3612
|
-
return null;
|
|
3613
|
-
}
|
|
3614
|
-
|
|
3615
3616
|
function normalizeUsage(u) {
|
|
3616
3617
|
const out = {};
|
|
3617
3618
|
for (const k of [
|
|
@@ -3694,13 +3695,6 @@ function isAllZeroUsage(u) {
|
|
|
3694
3695
|
return true;
|
|
3695
3696
|
}
|
|
3696
3697
|
|
|
3697
|
-
function totalsReset(curr, prev) {
|
|
3698
|
-
const currTotal = curr?.total_tokens;
|
|
3699
|
-
const prevTotal = prev?.total_tokens;
|
|
3700
|
-
if (!isFiniteNumber(currTotal) || !isFiniteNumber(prevTotal)) return false;
|
|
3701
|
-
return currTotal < prevTotal;
|
|
3702
|
-
}
|
|
3703
|
-
|
|
3704
3698
|
function isFiniteNumber(v) {
|
|
3705
3699
|
return typeof v === "number" && Number.isFinite(v);
|
|
3706
3700
|
}
|
|
@@ -4940,32 +4934,77 @@ function resolveKiroCliDbPath(env = process.env) {
|
|
|
4940
4934
|
// no hyphens. kiro-cli writes proper UUIDs; lock to the canonical shape.
|
|
4941
4935
|
const KIRO_CLI_SESSION_FILE_RE =
|
|
4942
4936
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.json$/i;
|
|
4943
|
-
|
|
4944
|
-
//
|
|
4945
|
-
//
|
|
4946
|
-
//
|
|
4947
|
-
|
|
4948
|
-
|
|
4937
|
+
// The directory prefix and direct messages.jsonl child are the stable
|
|
4938
|
+
// contract. Do not assume the suffix will always remain a UUID: Kiro has
|
|
4939
|
+
// already changed this layout once, and an opaque non-empty session id is
|
|
4940
|
+
// sufficient to keep discovery both bounded and forward-compatible.
|
|
4941
|
+
const KIRO_CLI_V2_SESSION_DIR_RE = /^sess_.+$/;
|
|
4942
|
+
|
|
4943
|
+
// Lists both Kiro CLI session layouts:
|
|
4944
|
+
// legacy: ~/.kiro/sessions/cli/{uuid}.json
|
|
4945
|
+
// 2.13+: ~/.kiro/sessions/{workspaceHash}/sess_{uuid}/messages.jsonl
|
|
4946
|
+
//
|
|
4947
|
+
// Legacy .json files are rewritten atomically per turn. The 2.13+ JSONL is
|
|
4948
|
+
// append-only and may be read while a session is active. Only the direct
|
|
4949
|
+
// messages.jsonl child is accepted; sub-executions and arbitrary nested JSONL
|
|
4950
|
+
// files are intentionally excluded.
|
|
4949
4951
|
//
|
|
4950
4952
|
// TASK-014: env.HOME is honored (symmetric with resolveKiroCliDbPath) so
|
|
4951
4953
|
// callers can redirect to a tmp home for hermetic tests/CI.
|
|
4952
4954
|
function resolveKiroCliSessionFiles(env = process.env) {
|
|
4953
4955
|
const home = env.HOME || require("node:os").homedir();
|
|
4954
4956
|
const kiroHome = env.KIRO_HOME || path.join(home, ".kiro");
|
|
4955
|
-
const
|
|
4956
|
-
if (!fssync.existsSync(
|
|
4957
|
+
const sessionsRoot = path.join(kiroHome, "sessions");
|
|
4958
|
+
if (!fssync.existsSync(sessionsRoot)) return [];
|
|
4957
4959
|
const files = [];
|
|
4960
|
+
|
|
4961
|
+
const legacyDir = path.join(sessionsRoot, "cli");
|
|
4958
4962
|
try {
|
|
4959
|
-
for (const entry of fssync.readdirSync(
|
|
4963
|
+
for (const entry of fssync.readdirSync(legacyDir)) {
|
|
4960
4964
|
// TASK-003: only canonical {uuid}.json files; backups, scratch,
|
|
4961
4965
|
// typos are skipped so they don't feed JSON.parse garbage.
|
|
4962
4966
|
if (!KIRO_CLI_SESSION_FILE_RE.test(entry)) continue;
|
|
4963
|
-
files.push(path.join(
|
|
4967
|
+
files.push(path.join(legacyDir, entry));
|
|
4964
4968
|
}
|
|
4965
4969
|
} catch {
|
|
4966
4970
|
// ignore read errors
|
|
4967
4971
|
}
|
|
4968
|
-
|
|
4972
|
+
|
|
4973
|
+
try {
|
|
4974
|
+
const workspaceDirs = fssync.readdirSync(sessionsRoot, {
|
|
4975
|
+
withFileTypes: true,
|
|
4976
|
+
});
|
|
4977
|
+
for (const workspace of workspaceDirs) {
|
|
4978
|
+
if (!workspace.isDirectory() || workspace.name === "cli") continue;
|
|
4979
|
+
const workspacePath = path.join(sessionsRoot, workspace.name);
|
|
4980
|
+
let sessionDirs;
|
|
4981
|
+
try {
|
|
4982
|
+
sessionDirs = fssync.readdirSync(workspacePath, {
|
|
4983
|
+
withFileTypes: true,
|
|
4984
|
+
});
|
|
4985
|
+
} catch {
|
|
4986
|
+
continue;
|
|
4987
|
+
}
|
|
4988
|
+
for (const session of sessionDirs) {
|
|
4989
|
+
if (
|
|
4990
|
+
!session.isDirectory() ||
|
|
4991
|
+
!KIRO_CLI_V2_SESSION_DIR_RE.test(session.name)
|
|
4992
|
+
) {
|
|
4993
|
+
continue;
|
|
4994
|
+
}
|
|
4995
|
+
const messagesPath = path.join(
|
|
4996
|
+
workspacePath,
|
|
4997
|
+
session.name,
|
|
4998
|
+
"messages.jsonl",
|
|
4999
|
+
);
|
|
5000
|
+
if (fssync.existsSync(messagesPath)) files.push(messagesPath);
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
} catch {
|
|
5004
|
+
// ignore read errors
|
|
5005
|
+
}
|
|
5006
|
+
|
|
5007
|
+
return files.sort();
|
|
4969
5008
|
}
|
|
4970
5009
|
|
|
4971
5010
|
// Build char-count maps from a .jsonl sibling file. Lets us approximate
|
|
@@ -5183,6 +5222,199 @@ async function readKiroCliSessionTurns(jsonPath) {
|
|
|
5183
5222
|
return flat;
|
|
5184
5223
|
}
|
|
5185
5224
|
|
|
5225
|
+
function kiroCliV2ContentChars(value) {
|
|
5226
|
+
if (typeof value === "string") return value.length;
|
|
5227
|
+
if (Array.isArray(value)) {
|
|
5228
|
+
return value.reduce(
|
|
5229
|
+
(sum, item) => sum + kiroCliV2ContentChars(item),
|
|
5230
|
+
0,
|
|
5231
|
+
);
|
|
5232
|
+
}
|
|
5233
|
+
if (!value || typeof value !== "object") return 0;
|
|
5234
|
+
for (const key of ["content", "text", "value", "parts", "entries"]) {
|
|
5235
|
+
if (value[key] !== undefined) {
|
|
5236
|
+
return kiroCliV2ContentChars(value[key]);
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
return 0;
|
|
5240
|
+
}
|
|
5241
|
+
|
|
5242
|
+
// Kiro CLI 2.13+ writes event-sourced sessions under
|
|
5243
|
+
// ~/.kiro/sessions/<workspaceHash>/sess_<uuid>/messages.jsonl. The records do
|
|
5244
|
+
// not carry token counts, but user/assistant text, turn boundaries, timestamps,
|
|
5245
|
+
// and assistant reasoningModelId are sufficient for the same 4 chars/token
|
|
5246
|
+
// approximation used by the legacy CLI reader.
|
|
5247
|
+
async function readKiroCliV2SessionTurns(messagesPath) {
|
|
5248
|
+
if (
|
|
5249
|
+
!messagesPath ||
|
|
5250
|
+
path.basename(messagesPath) !== "messages.jsonl" ||
|
|
5251
|
+
!fssync.existsSync(messagesPath)
|
|
5252
|
+
) {
|
|
5253
|
+
return [];
|
|
5254
|
+
}
|
|
5255
|
+
|
|
5256
|
+
const sessionDir = path.dirname(messagesPath);
|
|
5257
|
+
let sessionMeta = {};
|
|
5258
|
+
try {
|
|
5259
|
+
sessionMeta = JSON.parse(
|
|
5260
|
+
fssync.readFileSync(path.join(sessionDir, "session.json"), "utf8"),
|
|
5261
|
+
);
|
|
5262
|
+
} catch {
|
|
5263
|
+
// messages.jsonl is self-contained enough to parse without session.json.
|
|
5264
|
+
}
|
|
5265
|
+
|
|
5266
|
+
const sessionId =
|
|
5267
|
+
typeof sessionMeta?.id === "string" && sessionMeta.id
|
|
5268
|
+
? sessionMeta.id
|
|
5269
|
+
: path.basename(sessionDir);
|
|
5270
|
+
const fallbackModel =
|
|
5271
|
+
typeof sessionMeta?.modelId === "string" ? sessionMeta.modelId : null;
|
|
5272
|
+
const fallbackTimestamp = Date.parse(
|
|
5273
|
+
sessionMeta?.createdAt || sessionMeta?.lastModifiedAt || "",
|
|
5274
|
+
);
|
|
5275
|
+
|
|
5276
|
+
const flat = [];
|
|
5277
|
+
let stream;
|
|
5278
|
+
try {
|
|
5279
|
+
stream = fssync.createReadStream(messagesPath, { encoding: "utf8" });
|
|
5280
|
+
} catch {
|
|
5281
|
+
return flat;
|
|
5282
|
+
}
|
|
5283
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
5284
|
+
|
|
5285
|
+
let pendingUserChars = 0;
|
|
5286
|
+
let pendingUserTimestampMs = NaN;
|
|
5287
|
+
let turn = null;
|
|
5288
|
+
let fallbackTurnIndex = 0;
|
|
5289
|
+
|
|
5290
|
+
const flushTurn = () => {
|
|
5291
|
+
if (!turn) return;
|
|
5292
|
+
const hasUsage =
|
|
5293
|
+
turn.outputChars > 0 ||
|
|
5294
|
+
turn.reasoningChars > 0;
|
|
5295
|
+
const tsMs = Number.isFinite(turn.timestampMs)
|
|
5296
|
+
? turn.timestampMs
|
|
5297
|
+
: Number.isFinite(pendingUserTimestampMs)
|
|
5298
|
+
? pendingUserTimestampMs
|
|
5299
|
+
: fallbackTimestamp;
|
|
5300
|
+
if (hasUsage && Number.isFinite(tsMs) && tsMs > 0) {
|
|
5301
|
+
const requestId =
|
|
5302
|
+
turn.executionId ||
|
|
5303
|
+
`${sessionId}:v2:${fallbackTurnIndex}`;
|
|
5304
|
+
const messageIds = Array.from(turn.messageIds);
|
|
5305
|
+
flat.push({
|
|
5306
|
+
request_id: requestId,
|
|
5307
|
+
message_id: messageIds[0] || turn.executionId || null,
|
|
5308
|
+
all_message_ids: messageIds,
|
|
5309
|
+
session_id: sessionId,
|
|
5310
|
+
session_model_id: fallbackModel,
|
|
5311
|
+
model_id: turn.modelId || fallbackModel,
|
|
5312
|
+
request_start_timestamp_ms: tsMs,
|
|
5313
|
+
user_prompt_length: turn.inputChars,
|
|
5314
|
+
response_size: turn.outputChars,
|
|
5315
|
+
reasoning_size: turn.reasoningChars,
|
|
5316
|
+
});
|
|
5317
|
+
}
|
|
5318
|
+
fallbackTurnIndex++;
|
|
5319
|
+
turn = null;
|
|
5320
|
+
};
|
|
5321
|
+
|
|
5322
|
+
const startTurn = (payload, timestampMs) => {
|
|
5323
|
+
flushTurn();
|
|
5324
|
+
turn = {
|
|
5325
|
+
executionId:
|
|
5326
|
+
typeof payload?.executionId === "string" ? payload.executionId : null,
|
|
5327
|
+
timestampMs:
|
|
5328
|
+
Number.isFinite(timestampMs)
|
|
5329
|
+
? timestampMs
|
|
5330
|
+
: pendingUserTimestampMs,
|
|
5331
|
+
inputChars: pendingUserChars,
|
|
5332
|
+
outputChars: 0,
|
|
5333
|
+
reasoningChars: 0,
|
|
5334
|
+
modelId: null,
|
|
5335
|
+
messageIds: new Set(),
|
|
5336
|
+
};
|
|
5337
|
+
pendingUserChars = 0;
|
|
5338
|
+
pendingUserTimestampMs = NaN;
|
|
5339
|
+
};
|
|
5340
|
+
|
|
5341
|
+
try {
|
|
5342
|
+
for await (const line of rl) {
|
|
5343
|
+
if (!line || !line.trim()) continue;
|
|
5344
|
+
let event;
|
|
5345
|
+
try {
|
|
5346
|
+
event = JSON.parse(line);
|
|
5347
|
+
} catch {
|
|
5348
|
+
continue;
|
|
5349
|
+
}
|
|
5350
|
+
const payload = event?.payload;
|
|
5351
|
+
if (!payload || typeof payload !== "object") continue;
|
|
5352
|
+
const type =
|
|
5353
|
+
typeof payload.type === "string"
|
|
5354
|
+
? payload.type.toLowerCase()
|
|
5355
|
+
: "";
|
|
5356
|
+
const timestampMs = Date.parse(event.timestamp || "");
|
|
5357
|
+
|
|
5358
|
+
if (type === "user") {
|
|
5359
|
+
if (turn) flushTurn();
|
|
5360
|
+
pendingUserChars = kiroCliV2ContentChars(payload.content);
|
|
5361
|
+
pendingUserTimestampMs = timestampMs;
|
|
5362
|
+
continue;
|
|
5363
|
+
}
|
|
5364
|
+
|
|
5365
|
+
if (type === "turn_start") {
|
|
5366
|
+
startTurn(payload, timestampMs);
|
|
5367
|
+
continue;
|
|
5368
|
+
}
|
|
5369
|
+
|
|
5370
|
+
if (
|
|
5371
|
+
["assistant", "tool_call", "tool_result", "usage_summary"].includes(
|
|
5372
|
+
type,
|
|
5373
|
+
) &&
|
|
5374
|
+
!turn
|
|
5375
|
+
) {
|
|
5376
|
+
startTurn(payload, timestampMs);
|
|
5377
|
+
}
|
|
5378
|
+
if (!turn) continue;
|
|
5379
|
+
|
|
5380
|
+
if (
|
|
5381
|
+
typeof payload.executionId === "string" &&
|
|
5382
|
+
payload.executionId &&
|
|
5383
|
+
!turn.executionId
|
|
5384
|
+
) {
|
|
5385
|
+
turn.executionId = payload.executionId;
|
|
5386
|
+
}
|
|
5387
|
+
if (typeof event.id === "string" && event.id) {
|
|
5388
|
+
turn.messageIds.add(event.id);
|
|
5389
|
+
}
|
|
5390
|
+
|
|
5391
|
+
if (type === "assistant") {
|
|
5392
|
+
const chars = kiroCliV2ContentChars(payload.content);
|
|
5393
|
+
if (
|
|
5394
|
+
typeof payload.operationType === "string" &&
|
|
5395
|
+
payload.operationType.toLowerCase() === "reasoning"
|
|
5396
|
+
) {
|
|
5397
|
+
turn.reasoningChars += chars;
|
|
5398
|
+
} else {
|
|
5399
|
+
turn.outputChars += chars;
|
|
5400
|
+
}
|
|
5401
|
+
if (
|
|
5402
|
+
typeof payload.reasoningModelId === "string" &&
|
|
5403
|
+
payload.reasoningModelId
|
|
5404
|
+
) {
|
|
5405
|
+
turn.modelId = payload.reasoningModelId;
|
|
5406
|
+
}
|
|
5407
|
+
} else if (type === "turn_end") {
|
|
5408
|
+
flushTurn();
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
} catch {
|
|
5412
|
+
// Return complete turns parsed before a concurrent truncate/delete.
|
|
5413
|
+
}
|
|
5414
|
+
flushTurn();
|
|
5415
|
+
return flat;
|
|
5416
|
+
}
|
|
5417
|
+
|
|
5186
5418
|
// Canonicalize a Kiro-CLI-emitted model id so IDE and CLI rows collapse when
|
|
5187
5419
|
// they refer to the same underlying Bedrock model. Examples:
|
|
5188
5420
|
// anthropic.claude-sonnet-4-20250514-v1:0 -> claude-sonnet-4
|
|
@@ -5202,6 +5434,7 @@ function canonicalizeKiroCliModelId(raw) {
|
|
|
5202
5434
|
if (!name) return null;
|
|
5203
5435
|
name = name.toLowerCase();
|
|
5204
5436
|
if (name === "auto") return null;
|
|
5437
|
+
name = name.replace(/^(?:qdev|kiro)::/, "");
|
|
5205
5438
|
// Strip provider prefix (anthropic., aws., openai., or a full Bedrock ARN).
|
|
5206
5439
|
name = name.replace(
|
|
5207
5440
|
/^(?:arn:aws:bedrock:[^:]*:[^:]*:(?:foundation-model\/)?|anthropic\.|openai\.|aws\.)/,
|
|
@@ -5294,10 +5527,11 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5294
5527
|
const resolvedEnv = env || process.env;
|
|
5295
5528
|
const dbPath = resolveKiroCliDbPath(resolvedEnv);
|
|
5296
5529
|
|
|
5297
|
-
// Combine
|
|
5298
|
-
// namespace: historical rows from the SQLite DB
|
|
5299
|
-
// from ~/.kiro/sessions/cli/{uuid}.json
|
|
5300
|
-
//
|
|
5530
|
+
// Combine three sources under the same (source='kiro', cursors.kiroCli)
|
|
5531
|
+
// namespace: historical rows from the SQLite DB, legacy live session state
|
|
5532
|
+
// from ~/.kiro/sessions/cli/{uuid}.json, and Kiro CLI 2.13+ event logs at
|
|
5533
|
+
// ~/.kiro/sessions/<workspaceHash>/sess_<uuid>/messages.jsonl. Request ID
|
|
5534
|
+
// shapes differ:
|
|
5301
5535
|
// SQLite carries a persisted request_id UUID; session files synthesize
|
|
5302
5536
|
// `${sessionId}:${loop_id.rand}`. When kiro-cli migrates a live session
|
|
5303
5537
|
// into SQLite the same turn lands under a new request_id — the cross-
|
|
@@ -5309,8 +5543,11 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5309
5543
|
: [];
|
|
5310
5544
|
const sessionFilesList = resolveKiroCliSessionFiles(resolvedEnv);
|
|
5311
5545
|
let flatSessions = [];
|
|
5312
|
-
for (const
|
|
5313
|
-
const turns =
|
|
5546
|
+
for (const sessionPath of sessionFilesList) {
|
|
5547
|
+
const turns =
|
|
5548
|
+
path.basename(sessionPath) === "messages.jsonl"
|
|
5549
|
+
? await readKiroCliV2SessionTurns(sessionPath)
|
|
5550
|
+
: await readKiroCliSessionTurns(sessionPath);
|
|
5314
5551
|
for (const turn of turns) flatSessions.push(turn);
|
|
5315
5552
|
}
|
|
5316
5553
|
// Per-request state replaces the old seenIds set. Each entry captures
|
|
@@ -5382,7 +5619,14 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5382
5619
|
toRetract.push([reqId, prev, sid]);
|
|
5383
5620
|
}
|
|
5384
5621
|
for (const [reqId, prev, sid] of toRetract) {
|
|
5385
|
-
if (
|
|
5622
|
+
if (
|
|
5623
|
+
prev.input_tokens ||
|
|
5624
|
+
prev.output_tokens ||
|
|
5625
|
+
prev.reasoning_output_tokens
|
|
5626
|
+
) {
|
|
5627
|
+
const prevReasoning = toNonNegativeInt(
|
|
5628
|
+
prev.reasoning_output_tokens,
|
|
5629
|
+
);
|
|
5386
5630
|
const prevBucket = getHourlyBucket(
|
|
5387
5631
|
hourlyState,
|
|
5388
5632
|
"kiro",
|
|
@@ -5394,8 +5638,12 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5394
5638
|
cached_input_tokens: 0,
|
|
5395
5639
|
cache_creation_input_tokens: 0,
|
|
5396
5640
|
output_tokens: -prev.output_tokens,
|
|
5397
|
-
reasoning_output_tokens:
|
|
5398
|
-
total_tokens: -(
|
|
5641
|
+
reasoning_output_tokens: -prevReasoning,
|
|
5642
|
+
total_tokens: -(
|
|
5643
|
+
prev.input_tokens +
|
|
5644
|
+
prev.output_tokens +
|
|
5645
|
+
prevReasoning
|
|
5646
|
+
),
|
|
5399
5647
|
conversation_count: -1,
|
|
5400
5648
|
});
|
|
5401
5649
|
touchedBuckets.add(bucketKey("kiro", prev.model, prev.bucketStart));
|
|
@@ -5488,8 +5736,12 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5488
5736
|
|
|
5489
5737
|
const promptChars = toNonNegativeInt(r.user_prompt_length);
|
|
5490
5738
|
const responseChars = toNonNegativeInt(r.response_size);
|
|
5739
|
+
const reasoningChars = toNonNegativeInt(r.reasoning_size);
|
|
5491
5740
|
const approxInput = Math.floor(promptChars / KIRO_CLI_CHARS_PER_TOKEN);
|
|
5492
5741
|
const approxOutput = Math.floor(responseChars / KIRO_CLI_CHARS_PER_TOKEN);
|
|
5742
|
+
const approxReasoning = Math.floor(
|
|
5743
|
+
reasoningChars / KIRO_CLI_CHARS_PER_TOKEN,
|
|
5744
|
+
);
|
|
5493
5745
|
|
|
5494
5746
|
const tsMs = Number(r.request_start_timestamp_ms);
|
|
5495
5747
|
if (!Number.isFinite(tsMs) || tsMs <= 0) continue;
|
|
@@ -5502,37 +5754,52 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5502
5754
|
const model = canonical || "kiro-cli-agent";
|
|
5503
5755
|
|
|
5504
5756
|
// Fingerprint captures every field whose change should cause a re-bucket.
|
|
5505
|
-
const fingerprint =
|
|
5757
|
+
const fingerprint =
|
|
5758
|
+
`${promptChars}:${responseChars}:${reasoningChars}:${model}:${tsMs}`;
|
|
5506
5759
|
const prev = requestState[requestId];
|
|
5507
5760
|
if (prev && prev.fingerprint === fingerprint) continue; // unchanged
|
|
5508
5761
|
|
|
5509
5762
|
// Subtract the prior contribution (if any) from its prior bucket so the
|
|
5510
5763
|
// bucket's absolute totals reflect the CURRENT truth, not the historical
|
|
5511
5764
|
// truth. enqueueTouchedBuckets will emit the net delta at flush time.
|
|
5512
|
-
if (
|
|
5765
|
+
if (
|
|
5766
|
+
prev &&
|
|
5767
|
+
(
|
|
5768
|
+
prev.input_tokens ||
|
|
5769
|
+
prev.output_tokens ||
|
|
5770
|
+
prev.reasoning_output_tokens
|
|
5771
|
+
)
|
|
5772
|
+
) {
|
|
5773
|
+
const prevReasoning = toNonNegativeInt(
|
|
5774
|
+
prev.reasoning_output_tokens,
|
|
5775
|
+
);
|
|
5513
5776
|
const prevBucket = getHourlyBucket(hourlyState, "kiro", prev.model, prev.bucketStart);
|
|
5514
5777
|
addTotals(prevBucket.totals, {
|
|
5515
5778
|
input_tokens: -prev.input_tokens,
|
|
5516
5779
|
cached_input_tokens: 0,
|
|
5517
5780
|
cache_creation_input_tokens: 0,
|
|
5518
5781
|
output_tokens: -prev.output_tokens,
|
|
5519
|
-
reasoning_output_tokens:
|
|
5520
|
-
total_tokens: -(
|
|
5782
|
+
reasoning_output_tokens: -prevReasoning,
|
|
5783
|
+
total_tokens: -(
|
|
5784
|
+
prev.input_tokens +
|
|
5785
|
+
prev.output_tokens +
|
|
5786
|
+
prevReasoning
|
|
5787
|
+
),
|
|
5521
5788
|
conversation_count: -1,
|
|
5522
5789
|
});
|
|
5523
5790
|
touchedBuckets.add(bucketKey("kiro", prev.model, prev.bucketStart));
|
|
5524
5791
|
}
|
|
5525
5792
|
|
|
5526
5793
|
// Add the new contribution.
|
|
5527
|
-
if (approxInput > 0 || approxOutput > 0) {
|
|
5794
|
+
if (approxInput > 0 || approxOutput > 0 || approxReasoning > 0) {
|
|
5528
5795
|
const bucket = getHourlyBucket(hourlyState, "kiro", model, bucketStart);
|
|
5529
5796
|
addTotals(bucket.totals, {
|
|
5530
5797
|
input_tokens: approxInput,
|
|
5531
5798
|
cached_input_tokens: 0,
|
|
5532
5799
|
cache_creation_input_tokens: 0,
|
|
5533
5800
|
output_tokens: approxOutput,
|
|
5534
|
-
reasoning_output_tokens:
|
|
5535
|
-
total_tokens: approxInput + approxOutput,
|
|
5801
|
+
reasoning_output_tokens: approxReasoning,
|
|
5802
|
+
total_tokens: approxInput + approxOutput + approxReasoning,
|
|
5536
5803
|
conversation_count: 1,
|
|
5537
5804
|
});
|
|
5538
5805
|
touchedBuckets.add(bucketKey("kiro", model, bucketStart));
|
|
@@ -5551,6 +5818,7 @@ async function parseKiroCliIncremental({ sessionFiles, cursors, queuePath, onPro
|
|
|
5551
5818
|
model,
|
|
5552
5819
|
input_tokens: approxInput,
|
|
5553
5820
|
output_tokens: approxOutput,
|
|
5821
|
+
reasoning_output_tokens: approxReasoning,
|
|
5554
5822
|
...(r.session_id ? { session_id: r.session_id } : {}),
|
|
5555
5823
|
};
|
|
5556
5824
|
|