tokentracker-cli 0.26.2 → 0.26.3

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 (42) hide show
  1. package/dashboard/dist/assets/{ActivityHeatmap-BX2q-PW5.js → ActivityHeatmap-BQmAWAi7.js} +1 -1
  2. package/dashboard/dist/assets/{Card-CKdycEDk.js → Card-Cq6JCZM2.js} +1 -1
  3. package/dashboard/dist/assets/{DashboardPage-T_BImOGr.js → DashboardPage-CUt6YnjQ.js} +1 -1
  4. package/dashboard/dist/assets/{DevicePage-DqFrE2fo.js → DevicePage-BwZwER8Q.js} +1 -1
  5. package/dashboard/dist/assets/{DialogTitle-BPUtAz2U.js → DialogTitle-wuIq7mbf.js} +1 -1
  6. package/dashboard/dist/assets/{FadeIn-ortmqOpl.js → FadeIn-CEOcEh_z.js} +1 -1
  7. package/dashboard/dist/assets/{HeaderGithubStar-Cl4nchsC.js → HeaderGithubStar-BRQgDG4V.js} +1 -1
  8. package/dashboard/dist/assets/{IpCheckPage-CxRBCdXW.js → IpCheckPage-BrOzm4SC.js} +1 -1
  9. package/dashboard/dist/assets/{LandingPage-DoIw_oXY.js → LandingPage-vqu0eDLZ.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardAvatar-GMFZci05.js → LeaderboardAvatar-Am5bi8es.js} +1 -1
  11. package/dashboard/dist/assets/{LeaderboardPage-BzkrPpnp.js → LeaderboardPage-Bi-245fM.js} +3 -3
  12. package/dashboard/dist/assets/{LeaderboardProfileModal-CAcqZ15e.js → LeaderboardProfileModal-C8LQXrnF.js} +1 -1
  13. package/dashboard/dist/assets/{LeaderboardProfilePage-D7jyFQk5.js → LeaderboardProfilePage-31yIarGc.js} +1 -1
  14. package/dashboard/dist/assets/{LimitsPage-fdq2MG4P.js → LimitsPage-h3rjkQlR.js} +1 -1
  15. package/dashboard/dist/assets/{LocalOnlyNotice-XEv4_b68.js → LocalOnlyNotice-CebJL6tE.js} +1 -1
  16. package/dashboard/dist/assets/{LoginPage-CJ31pW6G.js → LoginPage-sRWAttYf.js} +1 -1
  17. package/dashboard/dist/assets/{PopoverPopup-cEzMmw1k.js → PopoverPopup-D5KBjI3r.js} +1 -1
  18. package/dashboard/dist/assets/{ProviderIcon-CkKiJH4q.js → ProviderIcon-CSMEjb0n.js} +1 -1
  19. package/dashboard/dist/assets/{SettingsPage-D--hzVuV.js → SettingsPage-B9x5wKaH.js} +1 -1
  20. package/dashboard/dist/assets/{SkillsPage-BTxPA7dW.js → SkillsPage-Cl76d9F3.js} +1 -1
  21. package/dashboard/dist/assets/{WidgetsPage-Bh4zs5u8.js → WidgetsPage-CfgHQjCv.js} +1 -1
  22. package/dashboard/dist/assets/{WrappedPage-COhqXKs7.js → WrappedPage--2jUGICx.js} +1 -1
  23. package/dashboard/dist/assets/{arrow-up-right-B0tVW8XM.js → arrow-up-right-ru062Kz1.js} +1 -1
  24. package/dashboard/dist/assets/check-CG2_3azC.js +1 -0
  25. package/dashboard/dist/assets/{chevron-down-Cz58bkoU.js → chevron-down-Czb08wHc.js} +1 -1
  26. package/dashboard/dist/assets/{download-Gv8d-j81.js → download-_X5qj-Bp.js} +1 -1
  27. package/dashboard/dist/assets/{info-bDEPb8dB.js → info-DBw8ck72.js} +1 -1
  28. package/dashboard/dist/assets/{main-tm3dyfMf.js → main-CYNmFfGa.js} +2 -2
  29. package/dashboard/dist/assets/{use-limits-display-prefs-BvHWn0OQ.js → use-limits-display-prefs-DDOdet4v.js} +1 -1
  30. package/dashboard/dist/assets/{use-native-settings-CI_YvzEo.js → use-native-settings-Bz5dJHlV.js} +1 -1
  31. package/dashboard/dist/assets/{use-reduced-motion-pCeztzVC.js → use-reduced-motion-7VZbLGR1.js} +1 -1
  32. package/dashboard/dist/assets/{use-usage-limits-CKWnCeca.js → use-usage-limits-CW7MLh5g.js} +1 -1
  33. package/dashboard/dist/assets/{useCurrency-DLoIpDjL.js → useCurrency-BF2b0rl-.js} +1 -1
  34. package/dashboard/dist/index.html +1 -1
  35. package/dashboard/dist/share.html +1 -1
  36. package/package.json +1 -1
  37. package/src/commands/sync.js +8 -6
  38. package/src/lib/claude-categorizer.js +59 -18
  39. package/src/lib/pricing/matcher.js +20 -0
  40. package/src/lib/pricing/seed-snapshot.json +1 -1
  41. package/src/lib/rollout.js +4 -7
  42. package/dashboard/dist/assets/check-Dhwj43_0.js +0 -1
@@ -1 +1 @@
1
- import{r as a}from"./main-tm3dyfMf.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
1
+ import{r as a}from"./main-CYNmFfGa.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
@@ -1 +1 @@
1
- import{C as a,S as x,r as n,aG as g,aH as m,aI as b,aJ as u,aK as f,aE as h}from"./main-tm3dyfMf.js";import{C as y}from"./Card-CKdycEDk.js";function p({checked:s,onChange:t,disabled:e,ariaLabel:i}){return a.jsx("button",{type:"button",role:"switch","aria-checked":s,"aria-label":i,onClick:t,disabled:e,className:x("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500 disabled:opacity-50 disabled:cursor-not-allowed",s?"bg-oai-brand-500":"bg-oai-gray-300 dark:bg-oai-gray-700"),children:a.jsx("span",{className:x("inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",s?"translate-x-[18px]":"translate-x-[3px]")})})}function j({label:s,hint:t,control:e}){return a.jsxs("div",{className:"flex items-center justify-between gap-4 py-3",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("div",{className:"text-sm text-oai-gray-900 dark:text-oai-gray-200",children:s}),t?a.jsx("div",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),a.jsx("div",{className:"shrink-0",children:e})]})}function N({title:s,subtitle:t,action:e,children:i}){return a.jsxs(y,{children:[a.jsxs("div",{className:"mb-3 flex items-start justify-between gap-4",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("h2",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:s}),t?a.jsx("p",{className:"mt-1 truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),e?a.jsx("div",{className:"shrink-0",children:e}):null]}),a.jsx("div",{className:"-mb-3 divide-y divide-oai-gray-200/60 dark:divide-oai-gray-800/60",children:i})]})}function w({options:s,value:t,onChange:e}){return a.jsx("div",{className:"inline-flex items-center rounded-lg border border-oai-gray-200 bg-oai-gray-50 p-0.5 dark:border-oai-gray-800 dark:bg-oai-gray-900",children:s.map(({value:i,label:d,Icon:l})=>{const r=t===i;return a.jsxs("button",{type:"button",onClick:()=>e(i),"aria-pressed":r,className:x("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",r?"bg-white text-oai-black shadow-sm dark:bg-oai-gray-800 dark:text-white":"text-oai-gray-500 hover:text-oai-black dark:text-oai-gray-400 dark:hover:text-white"),children:[l?a.jsx(l,{className:"h-3.5 w-3.5","aria-hidden":!0}):null,a.jsx("span",{children:d})]},i)})})}function S(){const[s,t]=n.useState(null),e=g()&&m();n.useEffect(()=>{if(!e)return;const r=b(o=>t(o));return u(),r},[e]);const i=n.useCallback((r,o)=>{e&&(t(c=>c&&{...c,[r]:o}),f(r,o))},[e]),d=n.useCallback(r=>{e&&h(r)},[e]),l=n.useCallback(()=>{e&&u()},[e]);return{available:e,settings:s,setSetting:i,runAction:d,refresh:l}}export{N as S,p as T,j as a,w as b,S as u};
1
+ import{C as a,S as x,r as n,aG as g,aH as m,aI as b,aJ as u,aK as f,aE as h}from"./main-CYNmFfGa.js";import{C as y}from"./Card-Cq6JCZM2.js";function p({checked:s,onChange:t,disabled:e,ariaLabel:i}){return a.jsx("button",{type:"button",role:"switch","aria-checked":s,"aria-label":i,onClick:t,disabled:e,className:x("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500 disabled:opacity-50 disabled:cursor-not-allowed",s?"bg-oai-brand-500":"bg-oai-gray-300 dark:bg-oai-gray-700"),children:a.jsx("span",{className:x("inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",s?"translate-x-[18px]":"translate-x-[3px]")})})}function j({label:s,hint:t,control:e}){return a.jsxs("div",{className:"flex items-center justify-between gap-4 py-3",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("div",{className:"text-sm text-oai-gray-900 dark:text-oai-gray-200",children:s}),t?a.jsx("div",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),a.jsx("div",{className:"shrink-0",children:e})]})}function N({title:s,subtitle:t,action:e,children:i}){return a.jsxs(y,{children:[a.jsxs("div",{className:"mb-3 flex items-start justify-between gap-4",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("h2",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:s}),t?a.jsx("p",{className:"mt-1 truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),e?a.jsx("div",{className:"shrink-0",children:e}):null]}),a.jsx("div",{className:"-mb-3 divide-y divide-oai-gray-200/60 dark:divide-oai-gray-800/60",children:i})]})}function w({options:s,value:t,onChange:e}){return a.jsx("div",{className:"inline-flex items-center rounded-lg border border-oai-gray-200 bg-oai-gray-50 p-0.5 dark:border-oai-gray-800 dark:bg-oai-gray-900",children:s.map(({value:i,label:d,Icon:l})=>{const r=t===i;return a.jsxs("button",{type:"button",onClick:()=>e(i),"aria-pressed":r,className:x("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",r?"bg-white text-oai-black shadow-sm dark:bg-oai-gray-800 dark:text-white":"text-oai-gray-500 hover:text-oai-black dark:text-oai-gray-400 dark:hover:text-white"),children:[l?a.jsx(l,{className:"h-3.5 w-3.5","aria-hidden":!0}):null,a.jsx("span",{children:d})]},i)})})}function S(){const[s,t]=n.useState(null),e=g()&&m();n.useEffect(()=>{if(!e)return;const r=b(o=>t(o));return u(),r},[e]);const i=n.useCallback((r,o)=>{e&&(t(c=>c&&{...c,[r]:o}),f(r,o))},[e]),d=n.useCallback(r=>{e&&h(r)},[e]),l=n.useCallback(()=>{e&&u()},[e]);return{available:e,settings:s,setSetting:i,runAction:d,refresh:l}}export{N as S,p as T,j as a,w as b,S as u};
@@ -1 +1 @@
1
- import{aL as t,aM as o,r,aN as s}from"./main-tm3dyfMf.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
1
+ import{aL as t,aM as o,r,aN as s}from"./main-CYNmFfGa.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
@@ -1 +1 @@
1
- import{r,am as l}from"./main-tm3dyfMf.js";function m(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{m as u};
1
+ import{r,am as l}from"./main-CYNmFfGa.js";function m(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{m as u};
@@ -1 +1 @@
1
- import{r as e,aB as t,aC as r,H as c}from"./main-tm3dyfMf.js";const a=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function u(){return e.useContext(t)??a}export{u};
1
+ import{r as e,aB as t,aC as r,H as c}from"./main-CYNmFfGa.js";const a=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function u(){return e.useContext(t)??a}export{u};
@@ -210,7 +210,7 @@
210
210
  ]
211
211
  }
212
212
  </script>
213
- <script type="module" crossorigin src="/assets/main-tm3dyfMf.js"></script>
213
+ <script type="module" crossorigin src="/assets/main-CYNmFfGa.js"></script>
214
214
  <link rel="stylesheet" crossorigin href="/assets/main-smnMFIqE.css">
215
215
  </head>
216
216
  <body>
@@ -51,7 +51,7 @@
51
51
  "description": "Shareable Token Tracker dashboard snapshot."
52
52
  }
53
53
  </script>
54
- <script type="module" crossorigin src="/assets/main-tm3dyfMf.js"></script>
54
+ <script type="module" crossorigin src="/assets/main-CYNmFfGa.js"></script>
55
55
  <link rel="stylesheet" crossorigin href="/assets/main-smnMFIqE.css">
56
56
  </head>
57
57
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.26.2",
3
+ "version": "0.26.3",
4
4
  "description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, Grok Build, oh-my-pi, pi, Craft Agents, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose)",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -104,10 +104,12 @@ const CLAUDE_MEM_OBSERVER_PATH_SEGMENT = "--claude-mem-observer-sessions";
104
104
  // (DeepSeek/Kimi/Mimo/MiniMax anthropic-compatible endpoints, plus Claude
105
105
  // Code's sub-agent / thinking transport paths). The repaired ground truth
106
106
  // was therefore inflated by 1.6–3.7x on those providers — v3 left it that
107
- // way. v4 re-runs the same five-step atomic repair against the corrected
108
- // `claudeMessageDedupKey()` (msgId is globally unique on its own per the
109
- // Anthropic protocol, so the reqId requirement was always unnecessary).
110
- const CLAUDE_GROUND_TRUTH_REPAIR_KEY = "claudeGroundTruthRepair_2026_05_v4";
107
+ // way.
108
+ // v6 re-runs the same atomic repair after making Claude zero-usage snapshots
109
+ // ineligible for dedup state. Mimo reuses message.id across zero/partial
110
+ // streaming snapshots and the final token-bearing response, so v4/v5 could
111
+ // preserve a tiny scar total instead of the ccusage-aligned ground truth.
112
+ const CLAUDE_GROUND_TRUTH_REPAIR_KEY = "claudeGroundTruthRepair_2026_05_v6";
111
113
 
112
114
  async function cmdSync(argv) {
113
115
  const opts = parseArgs(argv);
@@ -2045,7 +2047,7 @@ async function repairClaudeQueueFromGroundTruth({
2045
2047
  }
2046
2048
  uploadState.offset = 0;
2047
2049
  uploadState.updatedAt = new Date().toISOString();
2048
- uploadState.note = "reset_after_claude_repair_2026_05_v4";
2050
+ uploadState.note = "reset_after_claude_repair_2026_05_v6";
2049
2051
  await fs.writeFile(queueStatePath, JSON.stringify(uploadState));
2050
2052
  }
2051
2053
 
@@ -2112,7 +2114,7 @@ async function repairClaudeQueueFromGroundTruth({
2112
2114
  }
2113
2115
  st.offset = 0;
2114
2116
  st.updatedAt = new Date().toISOString();
2115
- st.note = "reset_after_claude_repair_2026_05_v4";
2117
+ st.note = "reset_after_claude_repair_2026_05_v6";
2116
2118
  await fs.writeFile(projectQueueStatePath, JSON.stringify(st));
2117
2119
  }
2118
2120
  }
@@ -54,6 +54,44 @@ function emptyCategoryMap() {
54
54
  return out;
55
55
  }
56
56
 
57
+ function toNonNegativeNumber(value) {
58
+ const n = Number(value || 0);
59
+ return Number.isFinite(n) ? Math.max(0, n) : 0;
60
+ }
61
+
62
+ function readClaudeUsageTotals(usage) {
63
+ if (!usage || typeof usage !== "object") return null;
64
+ const input_tokens = toNonNegativeNumber(usage.input_tokens);
65
+ const cached_input_tokens = toNonNegativeNumber(usage.cache_read_input_tokens);
66
+ const cache_creation_input_tokens = toNonNegativeNumber(usage.cache_creation_input_tokens);
67
+ const output_tokens = toNonNegativeNumber(usage.output_tokens);
68
+ const reasoning_output_tokens = toNonNegativeNumber(usage.reasoning_output_tokens);
69
+ const total_tokens =
70
+ input_tokens + cached_input_tokens + cache_creation_input_tokens + output_tokens;
71
+
72
+ return {
73
+ input_tokens,
74
+ cached_input_tokens,
75
+ cache_creation_input_tokens,
76
+ output_tokens,
77
+ reasoning_output_tokens,
78
+ total_tokens,
79
+ };
80
+ }
81
+
82
+ function hasNonZeroClaudeUsage(usage) {
83
+ const totals = readClaudeUsageTotals(usage);
84
+ if (!totals) return false;
85
+ return (
86
+ totals.input_tokens +
87
+ totals.cached_input_tokens +
88
+ totals.cache_creation_input_tokens +
89
+ totals.output_tokens +
90
+ totals.reasoning_output_tokens >
91
+ 0
92
+ );
93
+ }
94
+
57
95
  function extractExecCommands(content) {
58
96
  const commands = [];
59
97
  for (const block of Array.isArray(content) ? content : []) {
@@ -481,10 +519,19 @@ async function categorizeSessionFile(filePath, { fromIso, toIso, seenHashes }, b
481
519
  if (toIso && ts > toIso) continue;
482
520
 
483
521
  const hash = claudeMessageDedupKey(obj);
484
- if (hash) {
485
- if (seenHashes.has(hash)) continue;
486
- seenHashes.add(hash);
487
- }
522
+ if (hash && seenHashes.has(hash)) continue;
523
+
524
+ // Defer the hash-add until we've confirmed this entry carries non-zero
525
+ // usage. Mirrors the same fix in rollout.js parseClaudeFile. Without it,
526
+ // a zero-token streaming snapshot (e.g. Mimo's pre-response thinking
527
+ // entry that shares the final response's message.id) would claim the
528
+ // bare-msgId dedup key — Mimo has no requestId, so claudeMessageDedupKey
529
+ // falls back to bare msgId — and pre-empt the real token-bearing entry,
530
+ // collapsing Mimo into a tiny fraction of its real total.
531
+ const usage = obj?.message?.usage;
532
+ if (!hasNonZeroClaudeUsage(usage)) continue;
533
+
534
+ if (hash) seenHashes.add(hash);
488
535
 
489
536
  classifyOneMessage(obj, sessionState, breakdown, toolLedger, skillLedger, execLedger);
490
537
  counted += 1;
@@ -1195,7 +1242,8 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
1195
1242
  continue;
1196
1243
  }
1197
1244
  const usage = obj?.message?.usage;
1198
- if (!usage || typeof usage !== "object") continue;
1245
+ const totals = readClaudeUsageTotals(usage);
1246
+ if (!totals || !hasNonZeroClaudeUsage(usage)) continue;
1199
1247
 
1200
1248
  const hash = claudeMessageDedupKey(obj);
1201
1249
  if (hash) {
@@ -1208,25 +1256,18 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
1208
1256
  const hourStart = ts ? toUtcHalfHourStart(ts) : null;
1209
1257
  if (!hourStart) continue;
1210
1258
 
1211
- const inputTok = Math.max(0, Number(usage.input_tokens || 0));
1212
- const cacheRead = Math.max(0, Number(usage.cache_read_input_tokens || 0));
1213
- const cacheCreate = Math.max(0, Number(usage.cache_creation_input_tokens || 0));
1214
- const outputTok = Math.max(0, Number(usage.output_tokens || 0));
1215
- const reasoningTok = Math.max(0, Number(usage.reasoning_output_tokens || 0));
1216
- const total = inputTok + cacheRead + cacheCreate + outputTok;
1217
-
1218
1259
  const key = `${model}|${hourStart}`;
1219
1260
  let acc = buckets.get(key);
1220
1261
  if (!acc) {
1221
1262
  acc = bucketAccumulator();
1222
1263
  buckets.set(key, acc);
1223
1264
  }
1224
- acc.input_tokens += inputTok;
1225
- acc.cached_input_tokens += cacheRead;
1226
- acc.cache_creation_input_tokens += cacheCreate;
1227
- acc.output_tokens += outputTok;
1228
- acc.reasoning_output_tokens += reasoningTok;
1229
- acc.total_tokens += total;
1265
+ acc.input_tokens += totals.input_tokens;
1266
+ acc.cached_input_tokens += totals.cached_input_tokens;
1267
+ acc.cache_creation_input_tokens += totals.cache_creation_input_tokens;
1268
+ acc.output_tokens += totals.output_tokens;
1269
+ acc.reasoning_output_tokens += totals.reasoning_output_tokens;
1270
+ acc.total_tokens += totals.total_tokens;
1230
1271
  }
1231
1272
  rl.close();
1232
1273
  stream.close?.();
@@ -6,6 +6,7 @@
6
6
  // 3. CURATED alias (e.g. "auto" -> "composer-1")
7
7
  // 4. CURATED fuzzy substring (e.g. "kiro-future-xyz" matches via "kiro")
8
8
  // 5. LiteLLM suffix-strip (gpt-5-codex-high-fast -> gpt-5-codex)
9
+ // 5b. LiteLLM provider-prefix strip (mimo-v2.5-pro -> openrouter/xiaomi/mimo-v2.5-pro)
9
10
  // 6. LiteLLM reverse substring (longest-key first)
10
11
  // 7. null (caller decides what to do — typically zero-pricing + negative cache)
11
12
 
@@ -169,6 +170,25 @@ function lookupPricing(model, { curated, litellm, source } = {}) {
169
170
  }
170
171
  }
171
172
 
173
+ // 5b. LiteLLM provider-prefix strip. Queue rows store the bare model name
174
+ // (e.g. "mimo-v2.5-pro"), but LiteLLM keys are provider-qualified (e.g.
175
+ // "openrouter/xiaomi/mimo-v2.5-pro"), so the exact lookups above miss. Match
176
+ // any key whose path suffix equals the bare model. When several providers
177
+ // expose the same model, pick the lexicographically smallest key so the
178
+ // resolved price is deterministic and independent of JSON ordering. Runs
179
+ // AFTER curated alias/fuzzy so e.g. Cursor's "auto" still resolves to
180
+ // composer-1 rather than a LiteLLM "*/auto" entry.
181
+ if (litellm) {
182
+ const suffix = "/" + lower;
183
+ let best = null;
184
+ for (const key of Object.keys(litellm)) {
185
+ if (key.length > suffix.length && key.toLowerCase().endsWith(suffix)) {
186
+ if (best === null || key < best) best = key;
187
+ }
188
+ }
189
+ if (best) return { hit: true, source: "litellm:prefix-strip", value: litellm[best] };
190
+ }
191
+
172
192
  // 6. LiteLLM reverse substring (longest-key first)
173
193
  if (litellm) {
174
194
  const sorted = getSortedKeys(litellm);