tokentracker-cli 1.5.13 → 1.5.14
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-Ct0vfdYb.js → AchievementBadge-DqJWve1h.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-dfoRxqOv.js → AchievementsPage-CnuBayj3.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-DHFyssU0.js → AchievementsSection-WRU5TxAB.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-ChJYZMSq.js → BadgeDetailModal-B5ikEQik.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-C_qaJy1-.js → BotAnimated-CaMG6j-6.js} +1 -1
- package/dashboard/dist/assets/{Card-BhK2nmOu.js → Card-BL3vxENk.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-Ele1kkut.js → CloudCheckoutPage-CsUb8CJ7.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-C5DMqcoV.js → CloudPage-5ZiPFzeC.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-C_hxoIwL.js → CommandPalette-CYd9nXW-.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BSTqBIEU.js → CommunityStatsModal-BItox7Sz.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-DI4QK5EV.js → ConfirmModal-BE1ABR4z.js} +1 -1
- package/dashboard/dist/assets/{Controls-zK1rzS_8.js → Controls-BRqIsaHT.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-BdDnmc60.js → DashboardPage-Cm0YuBlx.js} +1 -1
- package/dashboard/dist/assets/{DateRangePopover-DqwgxpNF.js → DateRangePopover-CRwEu66e.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-C0fQKKuI.js → DevicePage-DFUPsGb_.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-C_cbyHpb.js → DialogClose-DALl4GmP.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-CO0hykis.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BcBpd--_.js → DialogTitle-BSR8ozyn.js} +1 -1
- package/dashboard/dist/assets/{ElectricLogo-Dw9gkExE.js → ElectricLogo-BtrycBUW.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-Ci69gBGl.js → FadeIn-CfhgvLVd.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-pb03c0kb.js → HeaderGithubStar-N8wqAdOf.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-Cqd0D789.js → HoverTooltip-CwdMYIzZ.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-qILbvOJn.js → IpCheckPage-DCRor9cb.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CCIwaAeu.js → LandingPage-s9xvTHSp.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-BwzABmKU.js → LeaderboardPage-CUj3WLMo.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-7kdzfw8r.js → LeaderboardProBadge-N4NOKZWj.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-uMnN-IGe.js → LeaderboardProfileModal-09HiUyxm.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CQNc4MES.js → LeaderboardProfilePage-Cw9_rodG.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-CJ1LImRm.js → LimitsPage-F8b6kZa0.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-Dm0l_F0I.js → LocalOnlyNotice-D1eEzDEv.js} +1 -1
- package/dashboard/dist/assets/{LoginCard--tUiBVtr.js → LoginCard-0sIovG_J.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-B1-5fFMY.js → LoginModal-BT3oMVak.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-BYl6NnZ8.js → LoginPage-CDsXdaE5.js} +1 -1
- package/dashboard/dist/assets/{PetPage-DJNGJZrR.js → PetPage-BEnC1azp.js} +2 -2
- package/dashboard/dist/assets/{PopoverPopup-aRAdIkTs.js → PopoverPopup-Dwzp-hyf.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-uiA-KBe2.js → ProviderIcon-Cu_lcuuu.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BZT353_T.js → ResetPasswordPage-DrpEscm1.js} +1 -1
- package/dashboard/dist/assets/{Select-CPC04j0g.js → Select-wIR4qvEI.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-D1vJVrFO.js → SelectItemText-CNuJgYps.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-FL3EDqmb.js → SelfHostPage-CWNLygpL.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-BohSSNai.js → ServiceStatusPage-BId9t16Q.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-Cf_HASvt.js → SessionsPage-C_klx7d8.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-B4ztKHnj.js → SettingsPage-nC73JaZ4.js} +3 -3
- package/dashboard/dist/assets/{SkillsPage-BFiQkY7O.js → SkillsPage-B2agou8Y.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BHVdTJAP.js → TokenGalaxy-BNXUWRwa.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup-BMtQyFn6.js → TooltipPopup--X2kExQ0.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-DN-g3CNm.js → TrendMonitor-BELHsTnz.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Bv32yu8M.js → WidgetsPage-DWp0SMXA.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-HVTOehNb.js → WrappedPage-DgwczKHe.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-DnYJww7c.js → __vite-browser-external-BHphZRSC.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-BOwAJQqL.js → agent-logos-Cfx5WzU5.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-CeXuUuES.js → arrow-left-D83SdANw.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-DOdBG1FZ.js → arrow-right-D7ye9bmJ.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-Dponssa4.js → arrow-up-right-CLmwEwwC.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-CsxE6ot0.js → calendar-days-D1qLlvh4.js} +1 -1
- package/dashboard/dist/assets/{check-BroNG_vu.js → check-BHJvi4ey.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-C5ZpGeO1.js → chevron-down-6RBPKNpt.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-BtS-hZTc.js → cloud-checkout-DgWcF-at.js} +1 -1
- package/dashboard/dist/assets/{composite-fgJbx88k.js → composite-D-0pfyMY.js} +1 -1
- package/dashboard/dist/assets/{copy-CHBuwNUs.js → copy-B6mJSQsD.js} +1 -1
- package/dashboard/dist/assets/{download-EKd2MHkv.js → download-D31Nn-bI.js} +1 -1
- package/dashboard/dist/assets/{external-link-GkSo9C78.js → external-link-C_lK9S3p.js} +1 -1
- package/dashboard/dist/assets/{flame-Cg96qJrE.js → flame-1ZxhHshT.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-ChG2_n4h.js → format-tokens-DJk25SkD.js} +1 -1
- package/dashboard/dist/assets/{gift-B3QDbWPE.js → gift-BUZzFh-1.js} +1 -1
- package/dashboard/dist/assets/{icons-BEAyMjau.js → icons-BVPThCa4.js} +1 -1
- package/dashboard/dist/assets/{index-B9a1fK8I.js → index-B-mxrdIz.js} +1 -1
- package/dashboard/dist/assets/{index-DFcQyAnr.js → index-C0zFsOWh.js} +1 -1
- package/dashboard/dist/assets/{index-B0kZThUB.js → index-CjPsyTl7.js} +1 -1
- package/dashboard/dist/assets/{index-Cuc8ZXgl.js → index-CzstoU6d.js} +1 -1
- package/dashboard/dist/assets/{info-796bNttV.js → info-DmU-vlJ6.js} +1 -1
- package/dashboard/dist/assets/{link-2-LYRIPLwz.js → link-2-BlBM7uw7.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-DpKC9Uel.js → loader-circle-Dhrb_Ocy.js} +1 -1
- package/dashboard/dist/assets/{local-usage-transfer-Ctbuc6U4.js → local-usage-transfer-BudmMtkd.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-C5YTeNW1.js → modifiers.esm-kmw1VM8W.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-DJUCfEvG.js → monitor-smartphone-DtjXNC_x.js} +1 -1
- package/dashboard/dist/assets/{play-LC-lnw6w.js → play-9w46lbjy.js} +1 -1
- package/dashboard/dist/assets/{plus-CDF0S5xd.js → plus-DAf_WGKT.js} +1 -1
- package/dashboard/dist/assets/{provider-display-CkITUZgR.js → provider-display-BZ0XlB2q.js} +1 -1
- package/dashboard/dist/assets/{react-BIpM-G7K.js → react-B01ZJuZY.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-C3Ea423q.js → safePolygon-B6_x12Gb.js} +1 -1
- package/dashboard/dist/assets/{search-C-tN1mTM.js → search-D3dW9GbV.js} +1 -1
- package/dashboard/dist/assets/{server-DJ4gq_fT.js → server-C2XZBar7.js} +1 -1
- package/dashboard/dist/assets/{shield-check-f75hry9N.js → shield-check-4FX5vbS7.js} +1 -1
- package/dashboard/dist/assets/{skills-api-CZ_vfnRL.js → skills-api-BvW_MUyg.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-mLJSvq3M.js → sortable.esm-CAa_BJgh.js} +1 -1
- package/dashboard/dist/assets/{store-CZV1KXw2.js → store-2AX2MNkz.js} +1 -1
- package/dashboard/dist/assets/{terminal-B9vOwCl9.js → terminal-CON7n_ir.js} +1 -1
- package/dashboard/dist/assets/{trash-BN9GbynA.js → trash-C2f88jSi.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-Bc2d8tqa.js → use-community-stats-CgQu43xV.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-_0H4Rk58.js → use-latest-request-guard-lShM46-3.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-Dln-Rxps.js → use-limits-display-prefs-8o7pbEg0.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-CpkcGzXp.js → use-native-settings-dN8shG3M.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DpGA5U4S.js → use-ordered-list-beJy2IqQ.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-S0bR_G1B.js +1 -0
- package/dashboard/dist/assets/{use-transform-JXJt1W74.js → use-transform-cJpAQr2d.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-BK_H1-b7.js → use-usage-limits-CuusZA6b.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-ilbtKs16.js → useCurrency-Cx9UsKJK.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-M35LtCGR.js → usePositioner-TEClK6mF.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BtTQjv7z.js → useTokenFormat-BNwTLiac.js} +1 -1
- package/dashboard/dist/assets/{zap-5xemt9EH.js → zap-CFgbVEhC.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/sync.js +2 -2
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +218 -55
- package/dashboard/dist/assets/DialogDescription--_wg9JdV.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-Cy21hg3t.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -24394,6 +24394,9 @@ async function parseTraeCnApiIncremental({
|
|
|
24394
24394
|
const DSH_SESSION_LOG_MAX_BYTES = 64 * 1024 * 1024;
|
|
24395
24395
|
const DSH_SESSION_TEXT_MAX_BYTES = 128 * 1024 * 1024;
|
|
24396
24396
|
const DSH_SOURCE = "dsh";
|
|
24397
|
+
// v2 excludes fork seeds and replayed assistant identities. Old ledgers need
|
|
24398
|
+
// a verified replacement, not a reset of the provider-wide hourly totals.
|
|
24399
|
+
const DSH_ACCOUNTING_VERSION = 2;
|
|
24397
24400
|
|
|
24398
24401
|
// Precedence mirrors the harness's own resolveDshHome: an explicit
|
|
24399
24402
|
// TokenTracker override, then the harness's $DSH_HOME, then `~/.dsh`. Tests
|
|
@@ -24760,20 +24763,48 @@ function legacyDshContributionsFromSnapshot(snapshot, previousState) {
|
|
|
24760
24763
|
previousState.lastSeq < -1 ||
|
|
24761
24764
|
previousState.inode !== snapshot.inode ||
|
|
24762
24765
|
!Number.isFinite(previousState.size) ||
|
|
24763
|
-
snapshot.size < previousState.size
|
|
24766
|
+
snapshot.size < previousState.size ||
|
|
24767
|
+
(snapshot.size === previousState.size && snapshot.mtimeMs !== previousState.mtimeMs)
|
|
24764
24768
|
) {
|
|
24765
24769
|
return null;
|
|
24766
24770
|
}
|
|
24767
24771
|
const parsed = extractDshSessionUsage(snapshot.text, -1);
|
|
24768
|
-
if (!parsed.complete || parsed.sessionId !== previousState.sessionId
|
|
24772
|
+
if (!parsed.complete || parsed.sessionId !== previousState.sessionId ||
|
|
24773
|
+
parsed.maxSeq < previousState.lastSeq) return null;
|
|
24769
24774
|
// The pre-ledger parser replayed unknown-sequence usage on every pass, so
|
|
24770
24775
|
// there is no safe prefix boundary for such a record. Defer instead.
|
|
24771
|
-
|
|
24776
|
+
const priorDeltas = previousState.accountingVersion === DSH_ACCOUNTING_VERSION
|
|
24777
|
+
? parsed.deltas : parsed.rawDeltas;
|
|
24778
|
+
if (priorDeltas.some((delta) => !Number.isSafeInteger(delta.seq))) return null;
|
|
24772
24779
|
return dshContributionsFromDeltas(
|
|
24773
|
-
|
|
24780
|
+
priorDeltas.filter((delta) => delta.seq <= previousState.lastSeq),
|
|
24774
24781
|
);
|
|
24775
24782
|
}
|
|
24776
24783
|
|
|
24784
|
+
// When the old and new artifacts differ, aggregate equality alone does not
|
|
24785
|
+
// establish that the replacement contains the historical events. Match the
|
|
24786
|
+
// count-only metadata as a multiset, allowing seq rewrites but no lost events.
|
|
24787
|
+
function dshRawUsageCoversPrefix(candidate, snapshot, previousState) {
|
|
24788
|
+
if (!snapshot?.text || !previousState) return false;
|
|
24789
|
+
const prefix = extractDshSessionUsage(snapshot.text).rawDeltas
|
|
24790
|
+
.filter((delta) => delta.seq != null && delta.seq <= previousState.lastSeq);
|
|
24791
|
+
const key = (delta) => JSON.stringify([
|
|
24792
|
+
delta.messageId, delta.model, delta.timeMs, delta.totals,
|
|
24793
|
+
]);
|
|
24794
|
+
const available = new Map();
|
|
24795
|
+
for (const delta of candidate || []) {
|
|
24796
|
+
const identity = key(delta);
|
|
24797
|
+
available.set(identity, (available.get(identity) || 0) + 1);
|
|
24798
|
+
}
|
|
24799
|
+
for (const delta of prefix) {
|
|
24800
|
+
const identity = key(delta);
|
|
24801
|
+
const count = available.get(identity) || 0;
|
|
24802
|
+
if (!count) return false;
|
|
24803
|
+
available.set(identity, count - 1);
|
|
24804
|
+
}
|
|
24805
|
+
return true;
|
|
24806
|
+
}
|
|
24807
|
+
|
|
24777
24808
|
function dshContributionsCoverPrior(prior, candidate) {
|
|
24778
24809
|
for (const [key, previous] of Object.entries(prior || {})) {
|
|
24779
24810
|
const current = candidate?.[key];
|
|
@@ -25167,17 +25198,20 @@ function isCompleteDshJsonLine(raw) {
|
|
|
25167
25198
|
return index === text.length;
|
|
25168
25199
|
}
|
|
25169
25200
|
|
|
25170
|
-
//
|
|
25171
|
-
//
|
|
25172
|
-
//
|
|
25173
|
-
|
|
25174
|
-
|
|
25175
|
-
const deltas = [];
|
|
25201
|
+
// Read routing metadata and counts only; never materialize message content.
|
|
25202
|
+
// rawDeltas preserves the former seq-only interpretation for migration proof.
|
|
25203
|
+
// deltas excludes the inherited prefix and repeated assistant message ids.
|
|
25204
|
+
function extractDshSessionUsage(text, lastSeq = -1, lastLine = -1) {
|
|
25205
|
+
const rawDeltas = [];
|
|
25176
25206
|
const watermark = Number.isFinite(lastSeq) ? lastSeq : -1;
|
|
25177
25207
|
let maxSeq = watermark;
|
|
25208
|
+
let maxLine = -1;
|
|
25178
25209
|
let sessionId = null;
|
|
25179
25210
|
let headerModel = null;
|
|
25180
25211
|
let complete = true;
|
|
25212
|
+
let isSeeded = false;
|
|
25213
|
+
let seedLength = null;
|
|
25214
|
+
let inheritedThrough = -1;
|
|
25181
25215
|
|
|
25182
25216
|
const lines = String(text || "").split("\n");
|
|
25183
25217
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -25185,29 +25219,37 @@ function extractDshSessionUsage(text, lastSeq = -1) {
|
|
|
25185
25219
|
if (!line || !line.trim()) continue;
|
|
25186
25220
|
if (!isCompleteDshJsonLine(line)) {
|
|
25187
25221
|
complete = false;
|
|
25188
|
-
//
|
|
25189
|
-
// complete prefix so a repaired record is retried before seq advances.
|
|
25222
|
+
// Do not acknowledge records after a corrupt/torn interior line.
|
|
25190
25223
|
break;
|
|
25191
25224
|
}
|
|
25225
|
+
maxLine = i;
|
|
25192
25226
|
const eventType = parseDshJsonString(findDshJsonProperty(line, "type"));
|
|
25193
25227
|
if (!eventType) continue;
|
|
25194
|
-
|
|
25228
|
+
const data = findDshJsonProperty(line, "data");
|
|
25195
25229
|
if (eventType === "session" || eventType === "session/start") {
|
|
25196
|
-
const id =
|
|
25197
|
-
parseDshJsonString(findDshJsonProperty(
|
|
25198
|
-
parseDshJsonString(findDshJsonProperty(
|
|
25199
|
-
parseDshJsonString(findDshJsonProperty(findDshJsonProperty(line, "data"), "sessionId"));
|
|
25230
|
+
const id = parseDshJsonString(findDshJsonProperty(line, "id")) ||
|
|
25231
|
+
parseDshJsonString(findDshJsonProperty(data, "id")) ||
|
|
25232
|
+
parseDshJsonString(findDshJsonProperty(data, "sessionId"));
|
|
25200
25233
|
if (id) sessionId = id;
|
|
25234
|
+
isSeeded = findDshJsonProperty(line, "isSeeded") === "true" ||
|
|
25235
|
+
findDshJsonProperty(data, "isSeeded") === "true";
|
|
25236
|
+
const topLength = parseDshJsonNumber(findDshJsonProperty(line, "seedLength"));
|
|
25237
|
+
const length = Number.isFinite(topLength) ? topLength :
|
|
25238
|
+
parseDshJsonNumber(findDshJsonProperty(data, "seedLength"));
|
|
25239
|
+
if (Number.isSafeInteger(length) && length >= 0) seedLength = length;
|
|
25201
25240
|
continue;
|
|
25202
25241
|
}
|
|
25203
25242
|
|
|
25204
25243
|
const seq = parseDshJsonNumber(findDshJsonProperty(line, "seq"));
|
|
25205
25244
|
const seqKnown = Number.isFinite(seq) && seq >= 0;
|
|
25206
25245
|
if (seqKnown && seq > maxSeq) maxSeq = seq;
|
|
25207
|
-
|
|
25208
|
-
const data = findDshJsonProperty(line, "data");
|
|
25209
25246
|
if (!data) continue;
|
|
25210
|
-
|
|
25247
|
+
if (eventType === "session/end-seed") {
|
|
25248
|
+
// Nested forks contain earlier markers. The LAST explicitly inherited
|
|
25249
|
+
// marker is the boundary; ordinary resume markers (data:{}) are not.
|
|
25250
|
+
if (findDshJsonProperty(data, "inherited") === "true") inheritedThrough = i;
|
|
25251
|
+
continue;
|
|
25252
|
+
}
|
|
25211
25253
|
if (eventType === "request/header") {
|
|
25212
25254
|
const header = findDshJsonProperty(data, "header");
|
|
25213
25255
|
const config = findDshJsonProperty(header, "config");
|
|
@@ -25216,39 +25258,46 @@ function extractDshSessionUsage(text, lastSeq = -1) {
|
|
|
25216
25258
|
if (normalized) headerModel = normalized;
|
|
25217
25259
|
continue;
|
|
25218
25260
|
}
|
|
25219
|
-
|
|
25220
25261
|
if (eventType !== "assistant/message" && eventType !== "message/assistant") continue;
|
|
25221
|
-
if (seqKnown && seq <= watermark) continue;
|
|
25222
|
-
|
|
25223
25262
|
const message = findDshJsonProperty(data, "message");
|
|
25224
25263
|
const source = message ? findDshJsonProperty(message, "source") : null;
|
|
25225
25264
|
const model = normalizeDshModelName(
|
|
25226
25265
|
(source && parseDshJsonString(findDshJsonProperty(source, "model"))) ||
|
|
25227
25266
|
parseDshJsonString(findDshJsonProperty(data, "model")),
|
|
25228
25267
|
) || headerModel;
|
|
25229
|
-
const totals = dshUsageToTotals(
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
),
|
|
25233
|
-
);
|
|
25268
|
+
const totals = dshUsageToTotals(parseDshUsageSlice(
|
|
25269
|
+
findDshJsonProperty(data, "usage") || findDshJsonProperty(line, "usage"),
|
|
25270
|
+
));
|
|
25234
25271
|
if (!model || !totals) continue;
|
|
25235
|
-
|
|
25236
|
-
const timeMs =
|
|
25237
|
-
parseDshJsonNumber(findDshJsonProperty(line, "time")) ||
|
|
25272
|
+
const timeMs = parseDshJsonNumber(findDshJsonProperty(line, "time")) ||
|
|
25238
25273
|
parseDshJsonNumber(findDshJsonProperty(line, "timestamp")) ||
|
|
25239
25274
|
parseDshJsonNumber(findDshJsonProperty(data, "time")) ||
|
|
25240
25275
|
parseDshJsonNumber(findDshJsonProperty(data, "timestamp"));
|
|
25241
25276
|
if (!Number.isFinite(timeMs) || timeMs <= 0) continue;
|
|
25242
|
-
|
|
25243
|
-
|
|
25277
|
+
const id = parseDshJsonString(findDshJsonProperty(message, "id"));
|
|
25278
|
+
rawDeltas.push({ seq: seqKnown ? seq : null, line: i,
|
|
25279
|
+
messageId: id?.trim() || null, model, timeMs, totals });
|
|
25244
25280
|
}
|
|
25245
25281
|
|
|
25246
|
-
|
|
25282
|
+
const seedComplete = !isSeeded || inheritedThrough >= 0 || seedLength != null;
|
|
25283
|
+
const seen = new Set();
|
|
25284
|
+
const deltas = rawDeltas.filter((delta) => {
|
|
25285
|
+
if (delta.messageId) {
|
|
25286
|
+
if (seen.has(delta.messageId)) return false;
|
|
25287
|
+
// Claim before applying the watermark: a replay at a newer seq is not new.
|
|
25288
|
+
seen.add(delta.messageId);
|
|
25289
|
+
}
|
|
25290
|
+
if (!seedComplete || delta.line <= inheritedThrough) return false;
|
|
25291
|
+
// Legacy seedLength is an exclusive seq boundary, not a line count.
|
|
25292
|
+
if (seedLength != null && (delta.seq == null || delta.seq < seedLength)) return false;
|
|
25293
|
+
return delta.seq != null ? delta.seq > watermark : delta.line > lastLine;
|
|
25294
|
+
});
|
|
25295
|
+
return { deltas, rawDeltas, maxSeq, maxLine, sessionId, complete, seedComplete };
|
|
25247
25296
|
}
|
|
25248
25297
|
|
|
25249
25298
|
// Incremental parser entrypoint. Mirrors the other passive JSONL readers:
|
|
25250
25299
|
// identity-check each file (inode/size/mtime), re-read + re-parse only when it
|
|
25251
|
-
// changed, dedup via
|
|
25300
|
+
// changed, dedup via message ownership plus seq/line watermarks, accumulate into hourly
|
|
25252
25301
|
// buckets, then flush touched buckets to the queue. Session contribution ledgers
|
|
25253
25302
|
// make artifact-path migrations replace one session instead of resetting DSH.
|
|
25254
25303
|
async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgress }) {
|
|
@@ -25262,6 +25311,9 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25262
25311
|
const storedSessionState =
|
|
25263
25312
|
dshState.sessions && typeof dshState.sessions === "object" ? dshState.sessions : {};
|
|
25264
25313
|
const sessionState = { ...storedSessionState };
|
|
25314
|
+
// Never prune ownership when an origin transcript disappears. Only opaque
|
|
25315
|
+
// message ids and session identities are persisted, not conversation text.
|
|
25316
|
+
const messageOwners = { ...(dshState.messageOwners || {}) };
|
|
25265
25317
|
|
|
25266
25318
|
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
25267
25319
|
// normalizeHourlyState preserves bucket objects for the other incremental
|
|
@@ -25287,6 +25339,22 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25287
25339
|
const presentFiles = new Set(files);
|
|
25288
25340
|
const total = files.length;
|
|
25289
25341
|
const deferredFilePaths = new Set();
|
|
25342
|
+
const legacyAliases = new Map();
|
|
25343
|
+
for (const [filePath, state] of Object.entries(fileState)) {
|
|
25344
|
+
if (!state?.sessionId || state.accountingVersion === DSH_ACCOUNTING_VERSION) continue;
|
|
25345
|
+
const aliases = legacyAliases.get(state.sessionId) || [];
|
|
25346
|
+
aliases.push(filePath);
|
|
25347
|
+
legacyAliases.set(state.sessionId, aliases);
|
|
25348
|
+
}
|
|
25349
|
+
for (const aliases of legacyAliases.values()) {
|
|
25350
|
+
if (aliases.length < 2) continue;
|
|
25351
|
+
// Older cursors cannot prove whether these alias paths were counted once
|
|
25352
|
+
// or independently. Keep both baselines instead of guessing a subtraction.
|
|
25353
|
+
for (const filePath of aliases) {
|
|
25354
|
+
deferredFilePaths.add(filePath);
|
|
25355
|
+
deferredMigrationPaths.set(filePath, "ambiguous-legacy-session-aliases");
|
|
25356
|
+
}
|
|
25357
|
+
}
|
|
25290
25358
|
let recordsProcessed = 0;
|
|
25291
25359
|
let eventsAggregated = 0;
|
|
25292
25360
|
|
|
@@ -25344,6 +25412,9 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25344
25412
|
const needsLedgerBackfill = Boolean(
|
|
25345
25413
|
previousSessionId && !previousSessionContributions && !previousFileContributions,
|
|
25346
25414
|
);
|
|
25415
|
+
const needsAccountingUpgrade = Boolean(
|
|
25416
|
+
prev && prev.accountingVersion !== DSH_ACCOUNTING_VERSION,
|
|
25417
|
+
);
|
|
25347
25418
|
let snapshot;
|
|
25348
25419
|
let parsed;
|
|
25349
25420
|
let fullParsed = null;
|
|
@@ -25352,7 +25423,7 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25352
25423
|
|
|
25353
25424
|
try {
|
|
25354
25425
|
snapshot = await readDshSessionSnapshot(filePath, {
|
|
25355
|
-
previous: needsLedgerBackfill ? null : prev,
|
|
25426
|
+
previous: needsLedgerBackfill || needsAccountingUpgrade ? null : prev,
|
|
25356
25427
|
});
|
|
25357
25428
|
if (!snapshot) {
|
|
25358
25429
|
// A resolver-selected replacement can disappear between discovery and
|
|
@@ -25360,10 +25431,11 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25360
25431
|
// contribution. This is deliberately separate from a thrown read
|
|
25361
25432
|
// error because a missing path returns null from the snapshot helper.
|
|
25362
25433
|
deferStaleMigrationPaths(filePath, "replacement-missing");
|
|
25434
|
+
if (needsAccountingUpgrade) deferredMigrationPaths.set(filePath, "history-missing");
|
|
25363
25435
|
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25364
25436
|
continue;
|
|
25365
25437
|
}
|
|
25366
|
-
if (snapshot.unchanged && !needsLedgerBackfill) {
|
|
25438
|
+
if (snapshot.unchanged && !needsLedgerBackfill && !needsAccountingUpgrade) {
|
|
25367
25439
|
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25368
25440
|
continue;
|
|
25369
25441
|
}
|
|
@@ -25385,18 +25457,18 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25385
25457
|
),
|
|
25386
25458
|
);
|
|
25387
25459
|
const lastSeq = fileReset || !Number.isFinite(prev?.lastSeq) ? -1 : prev.lastSeq;
|
|
25388
|
-
parsed = extractDshSessionUsage(snapshot.text, lastSeq);
|
|
25460
|
+
parsed = extractDshSessionUsage(snapshot.text, lastSeq, fileReset ? -1 : prev?.lastLine);
|
|
25389
25461
|
sessionChanged = Boolean(
|
|
25390
25462
|
prev &&
|
|
25391
25463
|
parsed.sessionId &&
|
|
25392
25464
|
parsed.sessionId !== previousSessionId,
|
|
25393
25465
|
);
|
|
25394
25466
|
if (sessionChanged) parsed = extractDshSessionUsage(snapshot.text, -1);
|
|
25395
|
-
if (!prev || needsLedgerBackfill || sessionChanged || fileReset) {
|
|
25467
|
+
if (!prev || needsLedgerBackfill || needsAccountingUpgrade || sessionChanged || fileReset) {
|
|
25396
25468
|
fullParsed = extractDshSessionUsage(snapshot.text, -1);
|
|
25397
|
-
if (!prev || sessionChanged || fileReset) parsed = fullParsed;
|
|
25469
|
+
if (!prev || needsAccountingUpgrade || sessionChanged || fileReset) parsed = fullParsed;
|
|
25398
25470
|
}
|
|
25399
|
-
if (!parsed.complete) snapshot.mtimeMs = -1;
|
|
25471
|
+
if (!parsed.complete || !parsed.seedComplete) snapshot.mtimeMs = -1;
|
|
25400
25472
|
} catch (error) {
|
|
25401
25473
|
if (process.env.TOKENTRACKER_DEBUG) {
|
|
25402
25474
|
process.stderr.write(`[dsh] skipped ${filePath}: ${error?.message || error}\n`);
|
|
@@ -25405,11 +25477,22 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25405
25477
|
// selected file list. Keep that cursor until the replacement is readable
|
|
25406
25478
|
// so a failed migration cannot turn into an untracked double-count.
|
|
25407
25479
|
deferStaleMigrationPaths(filePath, "replacement-read-failed");
|
|
25480
|
+
if (needsAccountingUpgrade) deferredMigrationPaths.set(filePath, "history-read-failed");
|
|
25408
25481
|
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25409
25482
|
continue;
|
|
25410
25483
|
}
|
|
25411
25484
|
|
|
25412
25485
|
const sessionId = typeof parsed.sessionId === "string" ? parsed.sessionId : null;
|
|
25486
|
+
if ((legacyAliases.get(sessionId)?.length || 0) > 1) {
|
|
25487
|
+
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25488
|
+
continue;
|
|
25489
|
+
}
|
|
25490
|
+
if (prev && !sessionId && (previousSessionId || needsAccountingUpgrade)) {
|
|
25491
|
+
deferredFilePaths.add(filePath);
|
|
25492
|
+
deferredMigrationPaths.set(filePath, "replacement-session-id-missing");
|
|
25493
|
+
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25494
|
+
continue;
|
|
25495
|
+
}
|
|
25413
25496
|
const stalePathWithoutId = sessionId
|
|
25414
25497
|
? null
|
|
25415
25498
|
: Object.keys(fileState).find(
|
|
@@ -25427,8 +25510,15 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25427
25510
|
const stalePath = staleFileForSession(sessionId, filePath);
|
|
25428
25511
|
const previousPath =
|
|
25429
25512
|
session?.lastPath && session.lastPath !== filePath ? session.lastPath : stalePath;
|
|
25513
|
+
if (previousPath && previousPath !== filePath) {
|
|
25514
|
+
// Another artifact currently owns this session ledger. Replacing that
|
|
25515
|
+
// ledger needs the full candidate, even if this alias has its own cursor.
|
|
25516
|
+
fullParsed = extractDshSessionUsage(snapshot.text);
|
|
25517
|
+
parsed = fullParsed;
|
|
25518
|
+
}
|
|
25430
25519
|
const resetCurrentFile = Boolean(fileReset && !sessionChanged);
|
|
25431
|
-
const
|
|
25520
|
+
const upgradeCurrentFile = needsAccountingUpgrade && !sessionChanged;
|
|
25521
|
+
const replacementPath = previousPath || (resetCurrentFile || upgradeCurrentFile ? filePath : null);
|
|
25432
25522
|
const sessionContributions = storedDshContributions(session);
|
|
25433
25523
|
const staleContributions = storedDshContributions(stalePath && fileState[stalePath]);
|
|
25434
25524
|
const fileContributions = storedDshContributions(prev);
|
|
@@ -25451,7 +25541,9 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25451
25541
|
// A pure rename preserves the same bytes. Reusing the parsed full
|
|
25452
25542
|
// contribution is safe here because replacement below subtracts and
|
|
25453
25543
|
// re-adds it; no sequence watermark is transferred.
|
|
25454
|
-
oldContributions = dshContributionsFromDeltas(
|
|
25544
|
+
oldContributions = dshContributionsFromDeltas(
|
|
25545
|
+
previousState.accountingVersion === DSH_ACCOUNTING_VERSION ? parsed.deltas : parsed.rawDeltas,
|
|
25546
|
+
);
|
|
25455
25547
|
}
|
|
25456
25548
|
|
|
25457
25549
|
// Cursors written before the contribution ledger existed can still be
|
|
@@ -25464,10 +25556,13 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25464
25556
|
const oldSnapshot = await readDshSessionSnapshot(previousPath).catch(() => null);
|
|
25465
25557
|
oldContributions = legacyDshContributionsFromSnapshot(oldSnapshot, oldState);
|
|
25466
25558
|
}
|
|
25559
|
+
if (sessionId && upgradeCurrentFile && !oldContributions) {
|
|
25560
|
+
oldContributions = legacyDshContributionsFromSnapshot(snapshot, prev);
|
|
25561
|
+
}
|
|
25467
25562
|
const replaceSession = Boolean(
|
|
25468
25563
|
sessionId &&
|
|
25469
25564
|
oldContributions &&
|
|
25470
|
-
(!prev || resetCurrentFile || (previousPath && previousPath !== filePath)),
|
|
25565
|
+
(!prev || resetCurrentFile || upgradeCurrentFile || (previousPath && previousPath !== filePath)),
|
|
25471
25566
|
);
|
|
25472
25567
|
const oldContributionCount = Object.keys(oldContributions || {}).length;
|
|
25473
25568
|
if (sessionId && replacementPath && !oldContributions) {
|
|
@@ -25479,20 +25574,25 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25479
25574
|
}
|
|
25480
25575
|
if (
|
|
25481
25576
|
replaceSession &&
|
|
25482
|
-
(!parsed.complete || (oldContributionCount > 0 && parsed.
|
|
25577
|
+
(!parsed.complete || !parsed.seedComplete || (oldContributionCount > 0 && parsed.rawDeltas.length === 0))
|
|
25483
25578
|
) {
|
|
25484
25579
|
// A readable header-only or torn replacement is not authoritative. Keep
|
|
25485
25580
|
// the old contribution until a complete replacement with usage arrives.
|
|
25486
25581
|
deferredFilePaths.add(replacementPath);
|
|
25487
25582
|
deferredMigrationPaths.set(
|
|
25488
25583
|
replacementPath,
|
|
25489
|
-
parsed.complete ? "replacement-
|
|
25584
|
+
!parsed.complete ? "replacement-incomplete" :
|
|
25585
|
+
!parsed.seedComplete ? "seed-boundary-unavailable" : "replacement-has-no-usage",
|
|
25490
25586
|
);
|
|
25491
25587
|
if (resetCurrentFile) fileState[filePath] = { ...prev, mtimeMs: -1 };
|
|
25492
25588
|
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25493
25589
|
continue;
|
|
25494
25590
|
}
|
|
25495
|
-
|
|
25591
|
+
// A correction may legitimately be lower or zero. Verify that the FULL
|
|
25592
|
+
// raw log still accounts for the old ledger before subtracting anything.
|
|
25593
|
+
// Comparing corrected usage to the old totals would reject the very repair
|
|
25594
|
+
// this migration must make; accepting a cropped log would erase history.
|
|
25595
|
+
const candidateContributions = dshContributionsFromDeltas(parsed.rawDeltas);
|
|
25496
25596
|
if (
|
|
25497
25597
|
replaceSession &&
|
|
25498
25598
|
oldContributionCount > 0 &&
|
|
@@ -25518,6 +25618,46 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25518
25618
|
continue;
|
|
25519
25619
|
}
|
|
25520
25620
|
|
|
25621
|
+
const owner = sessionId || dshSessionDirectoryKey(filePath);
|
|
25622
|
+
const acceptedDeltas = parsed.deltas.filter((delta) => {
|
|
25623
|
+
if (!delta.messageId) return true;
|
|
25624
|
+
const priorOwner = messageOwners[`a:${delta.messageId}`];
|
|
25625
|
+
return !priorOwner || (replaceSession && priorOwner === owner);
|
|
25626
|
+
});
|
|
25627
|
+
// Known ownership is stronger evidence than bucket totals. An equal or
|
|
25628
|
+
// larger divergent alias can still omit a previously counted message.
|
|
25629
|
+
const ownedIds = new Set(parsed.deltas.map((delta) => delta.messageId).filter(Boolean));
|
|
25630
|
+
const dropsOwnedId = replaceSession && Object.entries(messageOwners).some(([key, value]) =>
|
|
25631
|
+
value === owner && !ownedIds.has(key.slice(2)),
|
|
25632
|
+
);
|
|
25633
|
+
if (dropsOwnedId) {
|
|
25634
|
+
deferredFilePaths.add(replacementPath);
|
|
25635
|
+
deferredMigrationPaths.set(replacementPath, "replacement-drops-owned-message");
|
|
25636
|
+
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25637
|
+
continue;
|
|
25638
|
+
}
|
|
25639
|
+
const correctedContributions = dshContributionsFromDeltas(acceptedDeltas);
|
|
25640
|
+
if (replaceSession && !dshContributionsCoverPrior(oldContributions, correctedContributions)) {
|
|
25641
|
+
// Aggregate >= is not proof: a cropped transcript can substitute larger
|
|
25642
|
+
// unrelated/replayed usage for a lost event. Reconstruct EXACTLY the
|
|
25643
|
+
// acknowledged prefix before allowing a downward/zero correction.
|
|
25644
|
+
const baselineState = previousState || prev;
|
|
25645
|
+
let baselineSnapshot = snapshot;
|
|
25646
|
+
if (previousPath && !sameDshSessionMetadata(baselineState, snapshot)) {
|
|
25647
|
+
baselineSnapshot = await readDshSessionSnapshot(previousPath).catch(() => null);
|
|
25648
|
+
}
|
|
25649
|
+
const proven = legacyDshContributionsFromSnapshot(baselineSnapshot, baselineState);
|
|
25650
|
+
if (baselineState?.accountingVersion === DSH_ACCOUNTING_VERSION ||
|
|
25651
|
+
!proven || !dshContributionsCoverPrior(oldContributions, proven) ||
|
|
25652
|
+
!dshContributionsCoverPrior(proven, oldContributions) ||
|
|
25653
|
+
!dshRawUsageCoversPrefix(parsed.rawDeltas, baselineSnapshot, baselineState)) {
|
|
25654
|
+
deferredFilePaths.add(replacementPath);
|
|
25655
|
+
deferredMigrationPaths.set(replacementPath, "historical-prefix-unverified");
|
|
25656
|
+
reportProgress(idx, recordsProcessed, eventsAggregated, total);
|
|
25657
|
+
continue;
|
|
25658
|
+
}
|
|
25659
|
+
}
|
|
25660
|
+
|
|
25521
25661
|
if (replaceSession) {
|
|
25522
25662
|
applyDshContributions({
|
|
25523
25663
|
hourlyState,
|
|
@@ -25530,18 +25670,24 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25530
25670
|
|
|
25531
25671
|
let nextContributions = replaceSession
|
|
25532
25672
|
? {}
|
|
25533
|
-
: sessionContributions || fileContributions || {};
|
|
25534
|
-
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25538
|
-
|
|
25673
|
+
: sessionChanged ? {} : sessionContributions || fileContributions || {};
|
|
25674
|
+
// Backfill missing ledgers without re-adding the acknowledged prefix.
|
|
25675
|
+
if (fullParsed && !replaceSession && prev && !sessionChanged && !fileReset) {
|
|
25676
|
+
nextContributions = dshContributionsFromDeltas(fullParsed.deltas.filter((delta) =>
|
|
25677
|
+
(!delta.messageId || !messageOwners[`a:${delta.messageId}`] ||
|
|
25678
|
+
messageOwners[`a:${delta.messageId}`] === owner) &&
|
|
25679
|
+
(delta.seq != null ? delta.seq <= prev.lastSeq : delta.line <= prev.lastLine),
|
|
25680
|
+
));
|
|
25681
|
+
}
|
|
25682
|
+
|
|
25683
|
+
for (const delta of acceptedDeltas) {
|
|
25684
|
+
if (delta.messageId) messageOwners[`a:${delta.messageId}`] = owner;
|
|
25539
25685
|
const bucketStart = toUtcHalfHourStart(delta.timeMs);
|
|
25540
25686
|
if (!bucketStart) continue;
|
|
25541
25687
|
const bucket = getHourlyBucket(hourlyState, DSH_SOURCE, delta.model, bucketStart);
|
|
25542
25688
|
addTotals(bucket.totals, delta.totals);
|
|
25543
25689
|
touchedBuckets.add(bucketKey(DSH_SOURCE, delta.model, bucketStart));
|
|
25544
|
-
|
|
25690
|
+
addDshContribution(nextContributions, delta.model, bucketStart, delta.totals);
|
|
25545
25691
|
eventsAggregated += 1;
|
|
25546
25692
|
}
|
|
25547
25693
|
|
|
@@ -25552,12 +25698,15 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25552
25698
|
mtimeMs: snapshot.mtimeMs,
|
|
25553
25699
|
sessionId,
|
|
25554
25700
|
lastSeq: parsed.maxSeq,
|
|
25701
|
+
lastLine: parsed.maxLine,
|
|
25702
|
+
accountingVersion: DSH_ACCOUNTING_VERSION,
|
|
25555
25703
|
contributions: nextContributions,
|
|
25556
25704
|
updatedAt,
|
|
25557
25705
|
};
|
|
25558
25706
|
if (sessionId) {
|
|
25559
25707
|
sessionState[sessionId] = {
|
|
25560
25708
|
lastPath: filePath,
|
|
25709
|
+
accountingVersion: DSH_ACCOUNTING_VERSION,
|
|
25561
25710
|
contributions: nextContributions,
|
|
25562
25711
|
updatedAt,
|
|
25563
25712
|
};
|
|
@@ -25575,7 +25724,11 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25575
25724
|
// Retain pre-ledger identity until a replacement is verified. Its counted
|
|
25576
25725
|
// usage survives file deletion, so elapsed time cannot authorize replay.
|
|
25577
25726
|
// Current ledgers retain the same identity in sessionState instead.
|
|
25578
|
-
if (legacySessionId && !storedDshContributions(state) && !sessionState[legacySessionId])
|
|
25727
|
+
if ((legacySessionId && !storedDshContributions(state) && !sessionState[legacySessionId]) ||
|
|
25728
|
+
(!legacySessionId && state.accountingVersion !== DSH_ACCOUNTING_VERSION)) {
|
|
25729
|
+
if (state.accountingVersion !== DSH_ACCOUNTING_VERSION) {
|
|
25730
|
+
deferredMigrationPaths.set(filePath, "history-unverified");
|
|
25731
|
+
}
|
|
25579
25732
|
if (!Number.isFinite(Number(state?.missingSince))) {
|
|
25580
25733
|
fileState[filePath] = { ...state, missingSince: nowMs };
|
|
25581
25734
|
}
|
|
@@ -25584,10 +25737,20 @@ async function parseDshIncremental({ sessionFiles, cursors, queuePath, onProgres
|
|
|
25584
25737
|
delete fileState[filePath];
|
|
25585
25738
|
}
|
|
25586
25739
|
|
|
25740
|
+
// Missing/corrupt historical transcripts retain their ledger and are made
|
|
25741
|
+
// visible as deferrals, never treated as a provider-wide reset opportunity.
|
|
25742
|
+
for (const [id, session] of Object.entries(sessionState)) {
|
|
25743
|
+
if (session?.accountingVersion !== DSH_ACCOUNTING_VERSION) {
|
|
25744
|
+
const key = session?.lastPath || `session:${id}`;
|
|
25745
|
+
if (!deferredMigrationPaths.has(key)) deferredMigrationPaths.set(key, "history-unverified");
|
|
25746
|
+
}
|
|
25747
|
+
}
|
|
25587
25748
|
const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
|
|
25588
25749
|
const updatedAt = new Date().toISOString();
|
|
25589
25750
|
hourlyState.updatedAt = updatedAt;
|
|
25590
25751
|
cursors.hourly = hourlyState;
|
|
25752
|
+
dshState.accountingVersion = DSH_ACCOUNTING_VERSION;
|
|
25753
|
+
dshState.messageOwners = messageOwners;
|
|
25591
25754
|
dshState.files = fileState;
|
|
25592
25755
|
dshState.sessions = sessionState;
|
|
25593
25756
|
if (deferredMigrationPaths.size > 0) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,J as a}from"./__vite-browser-external-DnYJww7c.js";import{u as p}from"./DialogTitle-BcBpd--_.js";import{u as c}from"./composite-fgJbx88k.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,i=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as o}from"./__vite-browser-external-DnYJww7c.js";import{h as r,i as t,p as s}from"./react-BIpM-G7K.js";function i(){!r.current&&t();const[e]=o.useState(s.current);return e}export{i as u};
|