@cryptiklemur/lattice 5.10.0 → 5.11.0

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 (142) hide show
  1. package/dist/client/assets/{angular-html-BDIcxkJq.js → angular-html-BoFzmWT8.js} +1 -1
  2. package/dist/client/assets/{angular-ts-Bt22ouNH.js → angular-ts-DZnI8rKE.js} +1 -1
  3. package/dist/client/assets/{apl-p8qkxzEK.js → apl-DstVmncE.js} +1 -1
  4. package/dist/client/assets/{astro-CIaMc49M.js → astro-DTPCjzEx.js} +1 -1
  5. package/dist/client/assets/{blade-BR56EAMD.js → blade-6q42Ss3F.js} +1 -1
  6. package/dist/client/assets/{c-Dli0HzAh.js → c-BQDGJ-nQ.js} +1 -1
  7. package/dist/client/assets/{cobol-Cad15ECy.js → cobol-Dlh0WvsZ.js} +1 -1
  8. package/dist/client/assets/{coffee-DpyATEbF.js → coffee-DdQv129j.js} +1 -1
  9. package/dist/client/assets/{cpp-KN8_NFsf.js → cpp-DhbQJIv4.js} +1 -1
  10. package/dist/client/assets/{crystal-CuyGv0kh.js → crystal-C22kERUB.js} +1 -1
  11. package/dist/client/assets/{css-Cm3q4bxn.js → css-n31O5kHj.js} +1 -1
  12. package/dist/client/assets/{dist-BjxsMc4u.js → dist-D8okl7lw.js} +2 -2
  13. package/dist/client/assets/{edge-B6S7CSbx.js → edge-Cgwx-o_7.js} +1 -1
  14. package/dist/client/assets/{elixir-CNUy9H8T.js → elixir-DAGM2WKD.js} +1 -1
  15. package/dist/client/assets/{elm-CNfcWmb9.js → elm-BLw_7oO9.js} +1 -1
  16. package/dist/client/assets/{erb-DWebzDaI.js → erb-DCaNhYa7.js} +1 -1
  17. package/dist/client/assets/{git-rebase-B_Pt2ZBK.js → git-rebase-CNNhb8-g.js} +1 -1
  18. package/dist/client/assets/{glimmer-js-CVwoOd72.js → glimmer-js-BnZd88Wi.js} +1 -1
  19. package/dist/client/assets/{glimmer-ts-CjtFSxjz.js → glimmer-ts-DvFNbZu-.js} +1 -1
  20. package/dist/client/assets/{glsl-CP4rggAA.js → glsl-Dnrk_Jnx.js} +1 -1
  21. package/dist/client/assets/{graphql-Dbm6sAtp.js → graphql-DlWTPvCG.js} +1 -1
  22. package/dist/client/assets/{hack-Bj9y3SGf.js → hack-DQg1Ek33.js} +1 -1
  23. package/dist/client/assets/{haml-DRGrdf3f.js → haml-DSk45qIE.js} +1 -1
  24. package/dist/client/assets/{handlebars-CFKjcBMg.js → handlebars-DuLvATB2.js} +1 -1
  25. package/dist/client/assets/{html-Vcd4eHHg.js → html-D4DiUnLg.js} +1 -1
  26. package/dist/client/assets/{html-derivative-BF0YbD4L.js → html-derivative-CS5MZ6d9.js} +1 -1
  27. package/dist/client/assets/{http-CGVTa2NT.js → http-CkDncfer.js} +1 -1
  28. package/dist/client/assets/{hurl-B0GrsGqd.js → hurl-DU39oO3U.js} +1 -1
  29. package/dist/client/assets/{index-CX1tudsF.js → index-CHPfE1Zl.js} +129 -129
  30. package/dist/client/assets/index-DHUKmLLC.css +2 -0
  31. package/dist/client/assets/{java-BJHQqHsm.js → java-lntACKEu.js} +1 -1
  32. package/dist/client/assets/{javascript-CmuMsKrc.js → javascript-CxkFc6nV.js} +1 -1
  33. package/dist/client/assets/{jinja-JxCLeq1j.js → jinja-DolO2zO7.js} +1 -1
  34. package/dist/client/assets/{jison-BdgAUhei.js → jison-Cok5FPev.js} +1 -1
  35. package/dist/client/assets/{json-DtPissHL.js → json-BebuQPrq.js} +1 -1
  36. package/dist/client/assets/{jsx-DUAxxDkP.js → jsx-iLBaUyXr.js} +1 -1
  37. package/dist/client/assets/{julia-DxDlbL6e.js → julia-C5Dsc7cH.js} +1 -1
  38. package/dist/client/assets/{just-CVmAAx2R.js → just-DJYqq_9R.js} +1 -1
  39. package/dist/client/assets/{latex-uwxggTWA.js → latex-BTTYiKj1.js} +1 -1
  40. package/dist/client/assets/{liquid-xsETAJJy.js → liquid-DpAKCrOB.js} +1 -1
  41. package/dist/client/assets/{lua-B2Hh8PgD.js → lua-BZ6b1hko.js} +1 -1
  42. package/dist/client/assets/{marko-yDeGxD87.js → marko-D8VK6iGt.js} +1 -1
  43. package/dist/client/assets/{mdc-QMp4ieYR.js → mdc-Paa3XzwY.js} +1 -1
  44. package/dist/client/assets/{nginx-7gmRmcqz.js → nginx-C5k9mWtJ.js} +1 -1
  45. package/dist/client/assets/{nim-CA8SNY_7.js → nim-Dst6YSnE.js} +1 -1
  46. package/dist/client/assets/{perl-lx5nW4VC.js → perl-XhiCjgBp.js} +1 -1
  47. package/dist/client/assets/{php-DgHiW953.js → php-BcsPLnLU.js} +1 -1
  48. package/dist/client/assets/{pug-CbbB1vwb.js → pug-GLH9-eAJ.js} +1 -1
  49. package/dist/client/assets/{qml-COrzwCIh.js → qml-Cj_lJioE.js} +1 -1
  50. package/dist/client/assets/{r-Dv7pZJDH.js → r-B70aGYK5.js} +1 -1
  51. package/dist/client/assets/{razor-D2m8EDP5.js → razor-R3gub_zy.js} +1 -1
  52. package/dist/client/assets/{regexp-BXLT-jPc.js → regexp-itC0dIUJ.js} +1 -1
  53. package/dist/client/assets/{rst-_S6rrUYh.js → rst-DdyoV8E2.js} +1 -1
  54. package/dist/client/assets/{ruby-C3XO7tYY.js → ruby-BYBZsv66.js} +1 -1
  55. package/dist/client/assets/{sas-DP2k4iuN.js → sas-fqfqXqj1.js} +1 -1
  56. package/dist/client/assets/{scss-lhLFMXGn.js → scss-B-ELv6mu.js} +1 -1
  57. package/dist/client/assets/{shellscript-BYlBPHen.js → shellscript-BgB8TNw6.js} +1 -1
  58. package/dist/client/assets/{shellsession-CbVyQKWZ.js → shellsession-BLK2Dgkm.js} +1 -1
  59. package/dist/client/assets/{soy-Be8a0lHq.js → soy-C7_RmNrp.js} +1 -1
  60. package/dist/client/assets/{sql-2KxvU9YS.js → sql-AUgbUJq4.js} +1 -1
  61. package/dist/client/assets/{stata-BxlWftTS.js → stata-CIVqSIOr.js} +1 -1
  62. package/dist/client/assets/{surrealql-CJ-q86nR.js → surrealql-BzRQzc5S.js} +1 -1
  63. package/dist/client/assets/{svelte-Q1ml0OiY.js → svelte-BCIwEwtb.js} +1 -1
  64. package/dist/client/assets/{templ-BbfPZhtu.js → templ-C1hbwe4u.js} +1 -1
  65. package/dist/client/assets/{tex-Dcth4Gi6.js → tex-CI4tIsaP.js} +1 -1
  66. package/dist/client/assets/{ts-tags-BKhSOXI3.js → ts-tags-SUeikhEp.js} +1 -1
  67. package/dist/client/assets/{tsx-CS6iQ0XH.js → tsx-xkp7aIZs.js} +1 -1
  68. package/dist/client/assets/{twig-BHp31ZxS.js → twig-CGgBSAyc.js} +1 -1
  69. package/dist/client/assets/{typescript-16YJBTaO.js → typescript-O2YMTl_s.js} +1 -1
  70. package/dist/client/assets/{vue-CMKwTi4r.js → vue-DsNRxos1.js} +1 -1
  71. package/dist/client/assets/{vue-html-Dr8VUA2G.js → vue-html-CuY3t7bs.js} +1 -1
  72. package/dist/client/assets/{vue-vine-DZUqDerl.js → vue-vine-C6kSCKwY.js} +1 -1
  73. package/dist/client/assets/{xml-CBbBKKDC.js → xml-DafwzOLY.js} +1 -1
  74. package/dist/client/assets/{xsl-DWEX6PKX.js → xsl-1SGGZibr.js} +1 -1
  75. package/dist/client/assets/{yaml-DvKvvh3X.js → yaml-DSVhzmhr.js} +1 -1
  76. package/dist/client/index.html +2 -2
  77. package/dist/client/sw.js +1 -1
  78. package/dist/server/analytics/engine.js +241 -241
  79. package/dist/server/assets.js +4 -4
  80. package/dist/server/auth/passphrase.js +13 -13
  81. package/dist/server/config.js +7 -7
  82. package/dist/server/daemon.js +93 -93
  83. package/dist/server/features/brainstorm.js +42 -42
  84. package/dist/server/features/ralph-loop.js +33 -33
  85. package/dist/server/features/scheduler.js +53 -53
  86. package/dist/server/features/specs.js +54 -54
  87. package/dist/server/features/sticky-notes.js +17 -17
  88. package/dist/server/features/superpowers.js +24 -24
  89. package/dist/server/handlers/analytics.js +1 -1
  90. package/dist/server/handlers/attachment.js +32 -32
  91. package/dist/server/handlers/bookmarks.js +4 -4
  92. package/dist/server/handlers/brainstorm.js +4 -4
  93. package/dist/server/handlers/chat.js +54 -54
  94. package/dist/server/handlers/editor.js +13 -13
  95. package/dist/server/handlers/fs.js +51 -51
  96. package/dist/server/handlers/hooks.js +20 -20
  97. package/dist/server/handlers/loop.js +6 -6
  98. package/dist/server/handlers/memory.js +44 -44
  99. package/dist/server/handlers/mesh.js +60 -60
  100. package/dist/server/handlers/notes.js +7 -7
  101. package/dist/server/handlers/plugins.js +174 -174
  102. package/dist/server/handlers/project-settings.js +26 -26
  103. package/dist/server/handlers/scheduler.js +6 -6
  104. package/dist/server/handlers/session.js +24 -24
  105. package/dist/server/handlers/settings.js +21 -21
  106. package/dist/server/handlers/skills.js +91 -91
  107. package/dist/server/handlers/specs.js +51 -28
  108. package/dist/server/handlers/terminal.js +13 -13
  109. package/dist/server/handlers/themes.js +21 -21
  110. package/dist/server/handlers/update.js +17 -17
  111. package/dist/server/hooks/event_forward.sh +34 -0
  112. package/dist/server/hooks/post_tool_use.sh +26 -0
  113. package/dist/server/hooks/statusline.sh +26 -0
  114. package/dist/server/identity.js +6 -6
  115. package/dist/server/index.js +111 -111
  116. package/dist/server/logger.js +1 -1
  117. package/dist/server/mesh/connector.js +78 -78
  118. package/dist/server/mesh/crypto.js +20 -20
  119. package/dist/server/mesh/discovery.js +14 -14
  120. package/dist/server/mesh/pairing.js +30 -30
  121. package/dist/server/mesh/peers.js +10 -10
  122. package/dist/server/mesh/proxy.js +14 -14
  123. package/dist/server/mesh/session-sync.js +23 -23
  124. package/dist/server/project/bookmarks.js +11 -11
  125. package/dist/server/project/context-breakdown.js +70 -70
  126. package/dist/server/project/file-browser.js +17 -17
  127. package/dist/server/project/project-files.js +68 -68
  128. package/dist/server/project/registry.js +10 -10
  129. package/dist/server/project/sdk-bridge.js +157 -157
  130. package/dist/server/project/session.js +201 -199
  131. package/dist/server/project/terminal.js +15 -15
  132. package/dist/server/project/warmup.js +37 -37
  133. package/dist/server/push.js +11 -11
  134. package/dist/server/runtime.js +1 -1
  135. package/dist/server/tls.js +15 -15
  136. package/dist/server/tui.js +15 -15
  137. package/dist/server/update-checker.js +21 -21
  138. package/dist/server/ws/broadcast.js +18 -18
  139. package/dist/server/ws/router.js +17 -17
  140. package/dist/shared/constants.js +8 -8
  141. package/package.json +2 -2
  142. package/dist/client/assets/index-DlfI20Gn.css +0 -2
@@ -4,10 +4,10 @@ import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { estimateCost, projectPathToHash } from "../project/session.js";
6
6
  import { loadConfig } from "../config.js";
7
- var cache = new Map();
8
- var CACHE_TTL = 5 * 60 * 1000;
9
- var inflight = new Map();
10
- export var SECTION_KEYS = {
7
+ const cache = new Map();
8
+ const CACHE_TTL = 5 * 60 * 1000;
9
+ const inflight = new Map();
10
+ export const SECTION_KEYS = {
11
11
  summary: ["totalCost", "totalSessions", "totalTokens", "cacheHitRate", "avgSessionCost", "avgSessionDuration", "costOverTime", "cumulativeCost", "sessionsOverTime", "tokensOverTime", "cacheHitRateOverTime"],
12
12
  spending: ["costDistribution", "sessionBubbles", "modelUsage", "projectBreakdown"],
13
13
  usage: ["toolUsage", "responseTimeData", "contextUtilization", "tokenFlowSankey"],
@@ -26,15 +26,15 @@ function bucketModel(model) {
26
26
  function getPeriodCutoff(period) {
27
27
  if (period === "all")
28
28
  return 0;
29
- var now = Date.now();
30
- var hours = { "24h": 24, "7d": 168, "30d": 720, "90d": 2160 };
29
+ const now = Date.now();
30
+ const hours = { "24h": 24, "7d": 168, "30d": 720, "90d": 2160 };
31
31
  return now - (hours[period] || 0) * 60 * 60 * 1000;
32
32
  }
33
33
  function formatDate(ts) {
34
- var d = new Date(ts);
35
- var year = d.getFullYear();
36
- var month = String(d.getMonth() + 1).padStart(2, "0");
37
- var day = String(d.getDate()).padStart(2, "0");
34
+ const d = new Date(ts);
35
+ const year = d.getFullYear();
36
+ const month = String(d.getMonth() + 1).padStart(2, "0");
37
+ const day = String(d.getDate()).padStart(2, "0");
38
38
  return year + "-" + month + "-" + day;
39
39
  }
40
40
  function getCostBucket(cost) {
@@ -56,8 +56,8 @@ function getCostBucket(cost) {
56
56
  }
57
57
  function parseSessionText(text, sessionId, projectSlug) {
58
58
  try {
59
- var lines = text.split("\n");
60
- var data = {
59
+ const lines = text.split("\n");
60
+ const data = {
61
61
  id: sessionId,
62
62
  title: "",
63
63
  project: projectSlug,
@@ -73,22 +73,22 @@ function parseSessionText(text, sessionId, projectSlug) {
73
73
  responseTimePoints: [],
74
74
  contextMessages: [],
75
75
  };
76
- var lastUserTimestamp = 0;
77
- var assistantIndex = 0;
78
- for (var i = 0; i < lines.length; i++) {
79
- var line = lines[i].trim();
76
+ let lastUserTimestamp = 0;
77
+ let assistantIndex = 0;
78
+ for (let i = 0; i < lines.length; i++) {
79
+ const line = lines[i].trim();
80
80
  if (!line)
81
81
  continue;
82
- var parsed;
82
+ let parsed;
83
83
  try {
84
84
  parsed = JSON.parse(line);
85
85
  }
86
86
  catch {
87
87
  continue;
88
88
  }
89
- var timestamp = 0;
89
+ let timestamp = 0;
90
90
  if (typeof parsed.timestamp === "string") {
91
- var ts = new Date(parsed.timestamp).getTime();
91
+ const ts = new Date(parsed.timestamp).getTime();
92
92
  if (!isNaN(ts))
93
93
  timestamp = ts;
94
94
  }
@@ -99,24 +99,24 @@ function parseSessionText(text, sessionId, projectSlug) {
99
99
  data.endTime = timestamp;
100
100
  }
101
101
  if (parsed.type === "assistant") {
102
- var message = parsed.message;
102
+ const message = parsed.message;
103
103
  if (!message)
104
104
  continue;
105
- var usage = message.usage;
106
- var model = message.model || "";
105
+ const usage = message.usage;
106
+ const model = message.model || "";
107
107
  if (usage) {
108
- var inTok = usage.input_tokens || 0;
109
- var outTok = usage.output_tokens || 0;
110
- var cacheRead = usage.cache_read_input_tokens || 0;
111
- var cacheCreation = usage.cache_creation_input_tokens || 0;
108
+ const inTok = usage.input_tokens || 0;
109
+ const outTok = usage.output_tokens || 0;
110
+ const cacheRead = usage.cache_read_input_tokens || 0;
111
+ const cacheCreation = usage.cache_creation_input_tokens || 0;
112
112
  data.inputTokens += inTok;
113
113
  data.outputTokens += outTok;
114
114
  data.cacheReadTokens += cacheRead;
115
115
  data.cacheCreationTokens += cacheCreation;
116
- var cost = estimateCost(model, inTok, outTok, cacheRead, cacheCreation);
116
+ const cost = estimateCost(model, inTok, outTok, cacheRead, cacheCreation);
117
117
  data.cost += cost;
118
- var bucket = bucketModel(model);
119
- var existing = data.models.get(bucket);
118
+ const bucket = bucketModel(model);
119
+ const existing = data.models.get(bucket);
120
120
  if (existing) {
121
121
  existing.cost += cost;
122
122
  existing.tokens += inTok + outTok;
@@ -125,7 +125,7 @@ function parseSessionText(text, sessionId, projectSlug) {
125
125
  data.models.set(bucket, { cost: cost, tokens: inTok + outTok });
126
126
  }
127
127
  if (outTok > 0 && timestamp > 0 && lastUserTimestamp > 0) {
128
- var dur = timestamp - lastUserTimestamp;
128
+ const dur = timestamp - lastUserTimestamp;
129
129
  if (dur > 0 && dur < 600000) {
130
130
  data.responseTimePoints.push({ tokens: outTok, duration: dur, model: bucket });
131
131
  }
@@ -142,19 +142,19 @@ function parseSessionText(text, sessionId, projectSlug) {
142
142
  else if (parsed.type === "user") {
143
143
  if (timestamp > 0)
144
144
  lastUserTimestamp = timestamp;
145
- var userMsg = parsed.message;
145
+ const userMsg = parsed.message;
146
146
  if (!userMsg || !Array.isArray(userMsg.content))
147
147
  continue;
148
- var contentArr = userMsg.content;
149
- for (var j = 0; j < contentArr.length; j++) {
150
- var block = contentArr[j];
148
+ const contentArr = userMsg.content;
149
+ for (let j = 0; j < contentArr.length; j++) {
150
+ const block = contentArr[j];
151
151
  if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
152
- var toolName = block.name || "unknown";
152
+ const toolName = block.name || "unknown";
153
153
  data.tools.set(toolName, (data.tools.get(toolName) || 0) + 1);
154
154
  }
155
155
  }
156
156
  if (!data.title && Array.isArray(userMsg.content)) {
157
- for (var k = 0; k < contentArr.length; k++) {
157
+ for (let k = 0; k < contentArr.length; k++) {
158
158
  if (contentArr[k].type === "text" && typeof contentArr[k].text === "string") {
159
159
  data.title = contentArr[k].text.slice(0, 80);
160
160
  break;
@@ -173,7 +173,7 @@ function parseSessionText(text, sessionId, projectSlug) {
173
173
  }
174
174
  function parseSessionFile(filePath, sessionId, projectSlug) {
175
175
  try {
176
- var text = readFileSync(filePath, "utf-8");
176
+ const text = readFileSync(filePath, "utf-8");
177
177
  return parseSessionText(text, sessionId, projectSlug);
178
178
  }
179
179
  catch {
@@ -182,7 +182,7 @@ function parseSessionFile(filePath, sessionId, projectSlug) {
182
182
  }
183
183
  async function parseSessionFileAsync(filePath, sessionId, projectSlug) {
184
184
  try {
185
- var text = await readFile(filePath, "utf-8");
185
+ const text = await readFile(filePath, "utf-8");
186
186
  return parseSessionText(text, sessionId, projectSlug);
187
187
  }
188
188
  catch {
@@ -190,20 +190,20 @@ async function parseSessionFileAsync(filePath, sessionId, projectSlug) {
190
190
  }
191
191
  }
192
192
  function getSessionFilesForProject(projectPath, cutoff) {
193
- var hash = projectPathToHash(projectPath);
194
- var dir = join(homedir(), ".claude", "projects", hash);
193
+ const hash = projectPathToHash(projectPath);
194
+ const dir = join(homedir(), ".claude", "projects", hash);
195
195
  if (!existsSync(dir))
196
196
  return [];
197
- var files = [];
197
+ const files = [];
198
198
  try {
199
- var entries = readdirSync(dir);
200
- for (var i = 0; i < entries.length; i++) {
199
+ const entries = readdirSync(dir);
200
+ for (let i = 0; i < entries.length; i++) {
201
201
  if (!entries[i].endsWith(".jsonl"))
202
202
  continue;
203
- var filePath = join(dir, entries[i]);
203
+ const filePath = join(dir, entries[i]);
204
204
  if (cutoff && cutoff > 0) {
205
205
  try {
206
- var mtime = statSync(filePath).mtimeMs;
206
+ const mtime = statSync(filePath).mtimeMs;
207
207
  if (mtime < cutoff)
208
208
  continue;
209
209
  }
@@ -220,35 +220,35 @@ function getSessionFilesForProject(projectPath, cutoff) {
220
220
  return files;
221
221
  }
222
222
  function aggregate(sessions, period) {
223
- var cutoff = getPeriodCutoff(period);
224
- var filtered = [];
225
- for (var i = 0; i < sessions.length; i++) {
226
- var s = sessions[i];
227
- var sessionTime = s.endTime > 0 ? s.endTime : s.startTime;
223
+ const cutoff = getPeriodCutoff(period);
224
+ const filtered = [];
225
+ for (let i = 0; i < sessions.length; i++) {
226
+ const s = sessions[i];
227
+ const sessionTime = s.endTime > 0 ? s.endTime : s.startTime;
228
228
  if (sessionTime >= cutoff)
229
229
  filtered.push(s);
230
230
  }
231
- var totalCost = 0;
232
- var totalInput = 0;
233
- var totalOutput = 0;
234
- var totalCacheRead = 0;
235
- var totalCacheCreation = 0;
236
- var totalDuration = 0;
237
- var durationCount = 0;
238
- var dailyCost = new Map();
239
- var dailySessions = new Map();
240
- var dailyTokens = new Map();
241
- var dailyCacheHit = new Map();
242
- var modelStats = new Map();
243
- var projectStats = new Map();
244
- var toolStats = new Map();
245
- var costBuckets = new Map();
246
- var bucketOrder = ["<$0.01", "$0.01-0.05", "$0.05-0.10", "$0.10-0.50", "$0.50-1.00", "$1.00-5.00", "$5.00+"];
247
- for (var b = 0; b < bucketOrder.length; b++) {
231
+ let totalCost = 0;
232
+ let totalInput = 0;
233
+ let totalOutput = 0;
234
+ let totalCacheRead = 0;
235
+ let totalCacheCreation = 0;
236
+ let totalDuration = 0;
237
+ let durationCount = 0;
238
+ const dailyCost = new Map();
239
+ const dailySessions = new Map();
240
+ const dailyTokens = new Map();
241
+ const dailyCacheHit = new Map();
242
+ const modelStats = new Map();
243
+ const projectStats = new Map();
244
+ const toolStats = new Map();
245
+ const costBuckets = new Map();
246
+ const bucketOrder = ["<$0.01", "$0.01-0.05", "$0.05-0.10", "$0.10-0.50", "$0.50-1.00", "$1.00-5.00", "$5.00+"];
247
+ for (let b = 0; b < bucketOrder.length; b++) {
248
248
  costBuckets.set(bucketOrder[b], 0);
249
249
  }
250
- for (var si = 0; si < filtered.length; si++) {
251
- var sess = filtered[si];
250
+ for (let si = 0; si < filtered.length; si++) {
251
+ const sess = filtered[si];
252
252
  totalCost += sess.cost;
253
253
  totalInput += sess.inputTokens;
254
254
  totalOutput += sess.outputTokens;
@@ -258,8 +258,8 @@ function aggregate(sessions, period) {
258
258
  totalDuration += sess.endTime - sess.startTime;
259
259
  durationCount++;
260
260
  }
261
- var date = formatDate(sess.endTime > 0 ? sess.endTime : sess.startTime);
262
- var dc = dailyCost.get(date);
261
+ const date = formatDate(sess.endTime > 0 ? sess.endTime : sess.startTime);
262
+ let dc = dailyCost.get(date);
263
263
  if (!dc) {
264
264
  dc = { total: 0, opus: 0, sonnet: 0, haiku: 0, other: 0 };
265
265
  dailyCost.set(date, dc);
@@ -269,7 +269,7 @@ function aggregate(sessions, period) {
269
269
  dc[key] += val.cost;
270
270
  });
271
271
  dailySessions.set(date, (dailySessions.get(date) || 0) + 1);
272
- var dt = dailyTokens.get(date);
272
+ let dt = dailyTokens.get(date);
273
273
  if (!dt) {
274
274
  dt = { input: 0, output: 0, cacheRead: 0 };
275
275
  dailyTokens.set(date, dt);
@@ -277,7 +277,7 @@ function aggregate(sessions, period) {
277
277
  dt.input += sess.inputTokens;
278
278
  dt.output += sess.outputTokens;
279
279
  dt.cacheRead += sess.cacheReadTokens;
280
- var dch = dailyCacheHit.get(date);
280
+ let dch = dailyCacheHit.get(date);
281
281
  if (!dch) {
282
282
  dch = { cacheRead: 0, totalInput: 0 };
283
283
  dailyCacheHit.set(date, dch);
@@ -285,7 +285,7 @@ function aggregate(sessions, period) {
285
285
  dch.cacheRead += sess.cacheReadTokens;
286
286
  dch.totalInput += sess.inputTokens;
287
287
  sess.models.forEach(function (val, key) {
288
- var ms = modelStats.get(key);
288
+ let ms = modelStats.get(key);
289
289
  if (!ms) {
290
290
  ms = { sessions: 0, cost: 0, tokens: 0 };
291
291
  modelStats.set(key, ms);
@@ -294,7 +294,7 @@ function aggregate(sessions, period) {
294
294
  ms.cost += val.cost;
295
295
  ms.tokens += val.tokens;
296
296
  });
297
- var ps = projectStats.get(sess.project);
297
+ let ps = projectStats.get(sess.project);
298
298
  if (!ps) {
299
299
  ps = { cost: 0, sessions: 0, tokens: 0 };
300
300
  projectStats.set(sess.project, ps);
@@ -303,7 +303,7 @@ function aggregate(sessions, period) {
303
303
  ps.sessions++;
304
304
  ps.tokens += sess.inputTokens + sess.outputTokens;
305
305
  sess.tools.forEach(function (count, tool) {
306
- var ts = toolStats.get(tool);
306
+ let ts = toolStats.get(tool);
307
307
  if (!ts) {
308
308
  ts = { count: 0, totalCost: 0, sessions: 0 };
309
309
  toolStats.set(tool, ts);
@@ -312,23 +312,23 @@ function aggregate(sessions, period) {
312
312
  ts.totalCost += sess.cost;
313
313
  ts.sessions++;
314
314
  });
315
- var bucket = getCostBucket(sess.cost);
315
+ const bucket = getCostBucket(sess.cost);
316
316
  if (bucket) {
317
317
  costBuckets.set(bucket, (costBuckets.get(bucket) || 0) + 1);
318
318
  }
319
319
  }
320
- var totalTokensAll = totalInput + totalOutput + totalCacheRead + totalCacheCreation;
321
- var cacheHitRate = (totalInput + totalCacheRead) > 0 ? totalCacheRead / (totalInput + totalCacheRead) : 0;
322
- var dates = Array.from(dailyCost.keys()).sort();
323
- var costOverTime = [];
324
- var cumulativeCost = [];
325
- var sessionsOverTime = [];
326
- var tokensOverTime = [];
327
- var cacheHitRateOverTime = [];
328
- var cumTotal = 0;
329
- for (var di = 0; di < dates.length; di++) {
330
- var d = dates[di];
331
- var dcEntry = dailyCost.get(d);
320
+ const totalTokensAll = totalInput + totalOutput + totalCacheRead + totalCacheCreation;
321
+ const cacheHitRate = (totalInput + totalCacheRead) > 0 ? totalCacheRead / (totalInput + totalCacheRead) : 0;
322
+ const dates = Array.from(dailyCost.keys()).sort();
323
+ const costOverTime = [];
324
+ const cumulativeCost = [];
325
+ const sessionsOverTime = [];
326
+ const tokensOverTime = [];
327
+ const cacheHitRateOverTime = [];
328
+ let cumTotal = 0;
329
+ for (let di = 0; di < dates.length; di++) {
330
+ const d = dates[di];
331
+ const dcEntry = dailyCost.get(d);
332
332
  cumTotal += dcEntry.total;
333
333
  costOverTime.push({
334
334
  date: d,
@@ -340,32 +340,32 @@ function aggregate(sessions, period) {
340
340
  });
341
341
  cumulativeCost.push({ date: d, total: cumTotal });
342
342
  sessionsOverTime.push({ date: d, count: dailySessions.get(d) || 0 });
343
- var dtEntry = dailyTokens.get(d);
343
+ const dtEntry = dailyTokens.get(d);
344
344
  tokensOverTime.push({
345
345
  date: d,
346
346
  input: dtEntry ? dtEntry.input : 0,
347
347
  output: dtEntry ? dtEntry.output : 0,
348
348
  cacheRead: dtEntry ? dtEntry.cacheRead : 0,
349
349
  });
350
- var dchEntry = dailyCacheHit.get(d);
351
- var rate = dchEntry && (dchEntry.totalInput + dchEntry.cacheRead) > 0 ? dchEntry.cacheRead / (dchEntry.totalInput + dchEntry.cacheRead) : 0;
350
+ const dchEntry = dailyCacheHit.get(d);
351
+ const rate = dchEntry && (dchEntry.totalInput + dchEntry.cacheRead) > 0 ? dchEntry.cacheRead / (dchEntry.totalInput + dchEntry.cacheRead) : 0;
352
352
  cacheHitRateOverTime.push({ date: d, rate: rate });
353
353
  }
354
- var costDistribution = [];
355
- for (var bi = 0; bi < bucketOrder.length; bi++) {
354
+ const costDistribution = [];
355
+ for (let bi = 0; bi < bucketOrder.length; bi++) {
356
356
  costDistribution.push({
357
357
  bucket: bucketOrder[bi],
358
358
  count: costBuckets.get(bucketOrder[bi]) || 0,
359
359
  });
360
360
  }
361
- var sessionBubbles = [];
362
- var nonZeroCost = filtered.filter(function (s) { return s.cost > 0; });
363
- var sorted = nonZeroCost.slice().sort(function (a, b) {
361
+ const sessionBubbles = [];
362
+ const nonZeroCost = filtered.filter(function (s) { return s.cost > 0; });
363
+ const sorted = nonZeroCost.slice().sort(function (a, b) {
364
364
  return (b.endTime || b.startTime) - (a.endTime || a.startTime);
365
365
  });
366
- var bubbleCap = Math.min(sorted.length, 200);
367
- for (var sbi = 0; sbi < bubbleCap; sbi++) {
368
- var sb = sorted[sbi];
366
+ const bubbleCap = Math.min(sorted.length, 200);
367
+ for (let sbi = 0; sbi < bubbleCap; sbi++) {
368
+ const sb = sorted[sbi];
369
369
  sessionBubbles.push({
370
370
  id: sb.id,
371
371
  title: sb.title,
@@ -375,8 +375,8 @@ function aggregate(sessions, period) {
375
375
  project: sb.project,
376
376
  });
377
377
  }
378
- var modelUsage = [];
379
- var totalModelCost = totalCost || 1;
378
+ const modelUsage = [];
379
+ const totalModelCost = totalCost || 1;
380
380
  modelStats.forEach(function (val, key) {
381
381
  modelUsage.push({
382
382
  model: key,
@@ -387,7 +387,7 @@ function aggregate(sessions, period) {
387
387
  });
388
388
  });
389
389
  modelUsage.sort(function (a, b) { return b.cost - a.cost; });
390
- var projectBreakdown = [];
390
+ const projectBreakdown = [];
391
391
  projectStats.forEach(function (val, key) {
392
392
  projectBreakdown.push({
393
393
  project: key,
@@ -397,7 +397,7 @@ function aggregate(sessions, period) {
397
397
  });
398
398
  });
399
399
  projectBreakdown.sort(function (a, b) { return b.cost - a.cost; });
400
- var toolUsage = [];
400
+ const toolUsage = [];
401
401
  toolStats.forEach(function (val, key) {
402
402
  toolUsage.push({
403
403
  tool: key,
@@ -406,11 +406,11 @@ function aggregate(sessions, period) {
406
406
  });
407
407
  });
408
408
  toolUsage.sort(function (a, b) { return b.count - a.count; });
409
- var responseTimeData = [];
410
- for (var rti = 0; rti < filtered.length; rti++) {
411
- var rtSess = filtered[rti];
412
- for (var rtj = 0; rtj < rtSess.responseTimePoints.length; rtj++) {
413
- var rtp = rtSess.responseTimePoints[rtj];
409
+ const responseTimeData = [];
410
+ for (let rti = 0; rti < filtered.length; rti++) {
411
+ const rtSess = filtered[rti];
412
+ for (let rtj = 0; rtj < rtSess.responseTimePoints.length; rtj++) {
413
+ const rtp = rtSess.responseTimePoints[rtj];
414
414
  responseTimeData.push({ tokens: rtp.tokens, duration: rtp.duration, model: rtp.model, sessionId: rtSess.id });
415
415
  }
416
416
  if (responseTimeData.length >= 200)
@@ -418,23 +418,23 @@ function aggregate(sessions, period) {
418
418
  }
419
419
  if (responseTimeData.length > 200)
420
420
  responseTimeData.length = 200;
421
- var contextWindowSizes = { opus: 200000, sonnet: 200000, haiku: 200000, other: 200000 };
422
- var contextUtilization = [];
423
- var recentSessions = sorted.slice(0, 5);
424
- for (var cui = 0; cui < recentSessions.length; cui++) {
425
- var cuSess = recentSessions[cui];
426
- var runningTokens = 0;
427
- var primaryModel = "other";
428
- var maxModelTokens = 0;
421
+ const contextWindowSizes = { opus: 200000, sonnet: 200000, haiku: 200000, other: 200000 };
422
+ const contextUtilization = [];
423
+ const recentSessions = sorted.slice(0, 5);
424
+ for (let cui = 0; cui < recentSessions.length; cui++) {
425
+ const cuSess = recentSessions[cui];
426
+ let runningTokens = 0;
427
+ let primaryModel = "other";
428
+ let maxModelTokens = 0;
429
429
  cuSess.models.forEach(function (val, key) {
430
430
  if (val.tokens > maxModelTokens) {
431
431
  maxModelTokens = val.tokens;
432
432
  primaryModel = key;
433
433
  }
434
434
  });
435
- var windowSize = contextWindowSizes[primaryModel] || 200000;
436
- for (var cmj = 0; cmj < cuSess.contextMessages.length; cmj++) {
437
- var cm = cuSess.contextMessages[cmj];
435
+ const windowSize = contextWindowSizes[primaryModel] || 200000;
436
+ for (let cmj = 0; cmj < cuSess.contextMessages.length; cmj++) {
437
+ const cm = cuSess.contextMessages[cmj];
438
438
  runningTokens += cm.inputTokens;
439
439
  contextUtilization.push({
440
440
  messageIndex: cm.messageIndex,
@@ -444,7 +444,7 @@ function aggregate(sessions, period) {
444
444
  });
445
445
  }
446
446
  }
447
- var sankeyNodes = [
447
+ const sankeyNodes = [
448
448
  { name: "Input Tokens" },
449
449
  { name: "Cache Read" },
450
450
  { name: "Cache Creation" },
@@ -454,19 +454,19 @@ function aggregate(sessions, period) {
454
454
  { name: "Other" },
455
455
  { name: "Output Tokens" },
456
456
  ];
457
- var modelNodeMap = { opus: 3, sonnet: 4, haiku: 5, other: 6 };
458
- var sankeyLinks = [];
459
- var modelInputTotals = new Map();
460
- var modelCacheTotals = new Map();
461
- var modelCacheCreationTotals = new Map();
462
- var modelOutputTotals = new Map();
463
- for (var ski = 0; ski < filtered.length; ski++) {
464
- var skSess = filtered[ski];
465
- var skTotal = skSess.inputTokens + skSess.cacheReadTokens + skSess.cacheCreationTokens;
457
+ const modelNodeMap = { opus: 3, sonnet: 4, haiku: 5, other: 6 };
458
+ const sankeyLinks = [];
459
+ const modelInputTotals = new Map();
460
+ const modelCacheTotals = new Map();
461
+ const modelCacheCreationTotals = new Map();
462
+ const modelOutputTotals = new Map();
463
+ for (let ski = 0; ski < filtered.length; ski++) {
464
+ const skSess = filtered[ski];
465
+ const skTotal = skSess.inputTokens + skSess.cacheReadTokens + skSess.cacheCreationTokens;
466
466
  if (skTotal === 0)
467
467
  continue;
468
468
  skSess.models.forEach(function (val, key) {
469
- var proportion = val.tokens / (skTotal + skSess.outputTokens || 1);
469
+ const proportion = val.tokens / (skTotal + skSess.outputTokens || 1);
470
470
  modelInputTotals.set(key, (modelInputTotals.get(key) || 0) + skSess.inputTokens * proportion);
471
471
  modelCacheTotals.set(key, (modelCacheTotals.get(key) || 0) + skSess.cacheReadTokens * proportion);
472
472
  modelCacheCreationTotals.set(key, (modelCacheCreationTotals.get(key) || 0) + skSess.cacheCreationTokens * proportion);
@@ -474,11 +474,11 @@ function aggregate(sessions, period) {
474
474
  });
475
475
  }
476
476
  ["opus", "sonnet", "haiku", "other"].forEach(function (model) {
477
- var nodeIdx = modelNodeMap[model];
478
- var inputVal = Math.round(modelInputTotals.get(model) || 0);
479
- var cacheVal = Math.round(modelCacheTotals.get(model) || 0);
480
- var cacheCreationVal = Math.round(modelCacheCreationTotals.get(model) || 0);
481
- var outputVal = Math.round(modelOutputTotals.get(model) || 0);
477
+ const nodeIdx = modelNodeMap[model];
478
+ const inputVal = Math.round(modelInputTotals.get(model) || 0);
479
+ const cacheVal = Math.round(modelCacheTotals.get(model) || 0);
480
+ const cacheCreationVal = Math.round(modelCacheCreationTotals.get(model) || 0);
481
+ const outputVal = Math.round(modelOutputTotals.get(model) || 0);
482
482
  if (inputVal > 0)
483
483
  sankeyLinks.push({ source: 0, target: nodeIdx, value: inputVal });
484
484
  if (cacheVal > 0)
@@ -488,12 +488,12 @@ function aggregate(sessions, period) {
488
488
  if (outputVal > 0)
489
489
  sankeyLinks.push({ source: nodeIdx, target: 7, value: outputVal });
490
490
  });
491
- var tokenFlowSankey = { nodes: sankeyNodes, links: sankeyLinks };
492
- var activityCalendarMap = new Map();
493
- for (var aci = 0; aci < filtered.length; aci++) {
494
- var acSess = filtered[aci];
495
- var acDate = formatDate(acSess.endTime > 0 ? acSess.endTime : acSess.startTime);
496
- var acEntry = activityCalendarMap.get(acDate);
491
+ const tokenFlowSankey = { nodes: sankeyNodes, links: sankeyLinks };
492
+ const activityCalendarMap = new Map();
493
+ for (let aci = 0; aci < filtered.length; aci++) {
494
+ const acSess = filtered[aci];
495
+ const acDate = formatDate(acSess.endTime > 0 ? acSess.endTime : acSess.startTime);
496
+ let acEntry = activityCalendarMap.get(acDate);
497
497
  if (!acEntry) {
498
498
  acEntry = { count: 0, tokens: 0, cost: 0 };
499
499
  activityCalendarMap.set(acDate, acEntry);
@@ -502,14 +502,14 @@ function aggregate(sessions, period) {
502
502
  acEntry.tokens += acSess.inputTokens + acSess.outputTokens;
503
503
  acEntry.cost += acSess.cost;
504
504
  }
505
- var activityCalendar = [];
505
+ const activityCalendar = [];
506
506
  if (dates.length > 0) {
507
- var calStart = new Date(dates[0]);
508
- var calEnd = new Date(dates[dates.length - 1]);
509
- var calCursor = new Date(calStart);
507
+ const calStart = new Date(dates[0]);
508
+ const calEnd = new Date(dates[dates.length - 1]);
509
+ const calCursor = new Date(calStart);
510
510
  while (calCursor <= calEnd) {
511
- var calKey = formatDate(calCursor.getTime());
512
- var calData = activityCalendarMap.get(calKey);
511
+ const calKey = formatDate(calCursor.getTime());
512
+ const calData = activityCalendarMap.get(calKey);
513
513
  activityCalendar.push({
514
514
  date: calKey,
515
515
  count: calData ? calData.count : 0,
@@ -519,31 +519,31 @@ function aggregate(sessions, period) {
519
519
  calCursor.setDate(calCursor.getDate() + 1);
520
520
  }
521
521
  }
522
- var hourlyHeatmap = [];
523
- var heatmapGrid = new Map();
524
- for (var hmi = 0; hmi < filtered.length; hmi++) {
525
- var hmSess = filtered[hmi];
522
+ const hourlyHeatmap = [];
523
+ const heatmapGrid = new Map();
524
+ for (let hmi = 0; hmi < filtered.length; hmi++) {
525
+ const hmSess = filtered[hmi];
526
526
  if (hmSess.startTime <= 0)
527
527
  continue;
528
- var hmDate = new Date(hmSess.startTime);
529
- var hmDay = hmDate.getDay();
530
- var hmHour = hmDate.getHours();
531
- var hmKey = hmDay + ":" + hmHour;
528
+ const hmDate = new Date(hmSess.startTime);
529
+ const hmDay = hmDate.getDay();
530
+ const hmHour = hmDate.getHours();
531
+ const hmKey = hmDay + ":" + hmHour;
532
532
  heatmapGrid.set(hmKey, (heatmapGrid.get(hmKey) || 0) + 1);
533
533
  }
534
- for (var hd = 0; hd < 7; hd++) {
535
- for (var hh = 0; hh < 24; hh++) {
536
- var hhKey = hd + ":" + hh;
534
+ for (let hd = 0; hd < 7; hd++) {
535
+ for (let hh = 0; hh < 24; hh++) {
536
+ const hhKey = hd + ":" + hh;
537
537
  hourlyHeatmap.push({ day: hd, hour: hh, count: heatmapGrid.get(hhKey) || 0 });
538
538
  }
539
539
  }
540
- var sessionTimeline = [];
541
- var tlSorted = filtered
540
+ const sessionTimeline = [];
541
+ const tlSorted = filtered
542
542
  .filter(function (s) { return s.startTime > 0 && s.endTime > 0 && s.cost > 0; })
543
543
  .sort(function (a, b) { return b.startTime - a.startTime; });
544
- var tlCap = Math.min(tlSorted.length, 50);
545
- for (var tli = 0; tli < tlCap; tli++) {
546
- var tlSess = tlSorted[tli];
544
+ const tlCap = Math.min(tlSorted.length, 50);
545
+ for (let tli = 0; tli < tlCap; tli++) {
546
+ const tlSess = tlSorted[tli];
547
547
  sessionTimeline.push({
548
548
  id: tlSess.id,
549
549
  title: tlSess.title,
@@ -553,11 +553,11 @@ function aggregate(sessions, period) {
553
553
  cost: tlSess.cost,
554
554
  });
555
555
  }
556
- var dailySummaryMap = new Map();
557
- for (var dsi = 0; dsi < filtered.length; dsi++) {
558
- var dsSess = filtered[dsi];
559
- var dsDate = formatDate(dsSess.endTime > 0 ? dsSess.endTime : dsSess.startTime);
560
- var dsEntry = dailySummaryMap.get(dsDate);
556
+ const dailySummaryMap = new Map();
557
+ for (let dsi = 0; dsi < filtered.length; dsi++) {
558
+ const dsSess = filtered[dsi];
559
+ const dsDate = formatDate(dsSess.endTime > 0 ? dsSess.endTime : dsSess.startTime);
560
+ let dsEntry = dailySummaryMap.get(dsDate);
561
561
  if (!dsEntry) {
562
562
  dsEntry = { sessions: 0, cost: 0, tokens: 0, tools: new Map(), models: new Map() };
563
563
  dailySummaryMap.set(dsDate, dsEntry);
@@ -572,21 +572,21 @@ function aggregate(sessions, period) {
572
572
  dsEntry.models.set(key, (dsEntry.models.get(key) || 0) + val.cost);
573
573
  });
574
574
  }
575
- var dailySummaries = [];
576
- var dsSortedDates = Array.from(dailySummaryMap.keys()).sort();
577
- for (var dsdi = 0; dsdi < dsSortedDates.length; dsdi++) {
578
- var dsd = dsSortedDates[dsdi];
579
- var dsData = dailySummaryMap.get(dsd);
580
- var topTool = "";
581
- var topToolCount = 0;
575
+ const dailySummaries = [];
576
+ const dsSortedDates = Array.from(dailySummaryMap.keys()).sort();
577
+ for (let dsdi = 0; dsdi < dsSortedDates.length; dsdi++) {
578
+ const dsd = dsSortedDates[dsdi];
579
+ const dsData = dailySummaryMap.get(dsd);
580
+ let topTool = "";
581
+ let topToolCount = 0;
582
582
  dsData.tools.forEach(function (count, tool) {
583
583
  if (count > topToolCount) {
584
584
  topToolCount = count;
585
585
  topTool = tool;
586
586
  }
587
587
  });
588
- var modelMix = {};
589
- var modelTotal = 0;
588
+ const modelMix = {};
589
+ let modelTotal = 0;
590
590
  dsData.models.forEach(function (cost) { modelTotal += cost; });
591
591
  if (modelTotal > 0) {
592
592
  dsData.models.forEach(function (cost, model) {
@@ -602,7 +602,7 @@ function aggregate(sessions, period) {
602
602
  modelMix: modelMix,
603
603
  });
604
604
  }
605
- var toolTreemap = [];
605
+ const toolTreemap = [];
606
606
  toolStats.forEach(function (val, key) {
607
607
  toolTreemap.push({
608
608
  name: key,
@@ -611,29 +611,29 @@ function aggregate(sessions, period) {
611
611
  });
612
612
  });
613
613
  toolTreemap.sort(function (a, b) { return b.count - a.count; });
614
- var toolCategoryMap = {
614
+ const toolCategoryMap = {
615
615
  Read: "Read", Glob: "Read", Grep: "Read", LS: "Read",
616
616
  Edit: "Write", Write: "Write", MultiEdit: "Write",
617
617
  Bash: "Execute",
618
618
  Agent: "AI", Skill: "AI",
619
619
  };
620
- var toolSunburst = [];
620
+ const toolSunburst = [];
621
621
  toolStats.forEach(function (val, key) {
622
- var category = toolCategoryMap[key] || "Other";
622
+ const category = toolCategoryMap[key] || "Other";
623
623
  toolSunburst.push({ name: key, category: category, count: val.count });
624
624
  });
625
625
  toolSunburst.sort(function (a, b) { return b.count - a.count; });
626
- var totalToolCalls = 0;
626
+ let totalToolCalls = 0;
627
627
  toolStats.forEach(function (val) { totalToolCalls += val.count; });
628
- var permissionStats = {
628
+ const permissionStats = {
629
629
  allowed: totalToolCalls,
630
630
  denied: 0,
631
631
  alwaysAllowed: 0,
632
632
  };
633
- var projectRadarMap = new Map();
634
- for (var pri = 0; pri < filtered.length; pri++) {
635
- var prSess = filtered[pri];
636
- var prEntry = projectRadarMap.get(prSess.project);
633
+ const projectRadarMap = new Map();
634
+ for (let pri = 0; pri < filtered.length; pri++) {
635
+ const prSess = filtered[pri];
636
+ let prEntry = projectRadarMap.get(prSess.project);
637
637
  if (!prEntry) {
638
638
  prEntry = { cost: 0, sessions: 0, totalDuration: 0, durationCount: 0, tools: new Set(), totalTokens: 0 };
639
639
  projectRadarMap.set(prSess.project, prEntry);
@@ -647,7 +647,7 @@ function aggregate(sessions, period) {
647
647
  }
648
648
  prSess.tools.forEach(function (_count, tool) { prEntry.tools.add(tool); });
649
649
  }
650
- var projectRadar = [];
650
+ const projectRadar = [];
651
651
  projectRadarMap.forEach(function (val, key) {
652
652
  projectRadar.push({
653
653
  project: key,
@@ -661,27 +661,27 @@ function aggregate(sessions, period) {
661
661
  projectRadar.sort(function (a, b) { return b.cost - a.cost; });
662
662
  if (projectRadar.length > 5)
663
663
  projectRadar.length = 5;
664
- var contextWindowSizesForComplexity = { opus: 200000, sonnet: 200000, haiku: 200000, other: 200000 };
665
- var sessionComplexity = [];
666
- for (var sci = 0; sci < filtered.length; sci++) {
667
- var scSess = filtered[sci];
668
- var scUniqueTools = scSess.tools.size;
669
- var scMessages = scSess.contextMessages.length;
670
- var scRunning = 0;
671
- var scPrimaryModel = "other";
672
- var scMaxTokens = 0;
664
+ const contextWindowSizesForComplexity = { opus: 200000, sonnet: 200000, haiku: 200000, other: 200000 };
665
+ const sessionComplexity = [];
666
+ for (let sci = 0; sci < filtered.length; sci++) {
667
+ const scSess = filtered[sci];
668
+ const scUniqueTools = scSess.tools.size;
669
+ const scMessages = scSess.contextMessages.length;
670
+ let scRunning = 0;
671
+ let scPrimaryModel = "other";
672
+ let scMaxTokens = 0;
673
673
  scSess.models.forEach(function (val, key) {
674
674
  if (val.tokens > scMaxTokens) {
675
675
  scMaxTokens = val.tokens;
676
676
  scPrimaryModel = key;
677
677
  }
678
678
  });
679
- var scWindowSize = contextWindowSizesForComplexity[scPrimaryModel] || 200000;
680
- for (var scmi = 0; scmi < scSess.contextMessages.length; scmi++) {
679
+ const scWindowSize = contextWindowSizesForComplexity[scPrimaryModel] || 200000;
680
+ for (let scmi = 0; scmi < scSess.contextMessages.length; scmi++) {
681
681
  scRunning += scSess.contextMessages[scmi].inputTokens;
682
682
  }
683
- var scContextPercent = Math.min((scRunning / scWindowSize) * 100, 100);
684
- var scScore = (scMessages * 1) + (scUniqueTools * 5) + (scContextPercent * 0.5);
683
+ const scContextPercent = Math.min((scRunning / scWindowSize) * 100, 100);
684
+ const scScore = (scMessages * 1) + (scUniqueTools * 5) + (scContextPercent * 0.5);
685
685
  sessionComplexity.push({
686
686
  id: scSess.id,
687
687
  title: scSess.title,
@@ -731,54 +731,54 @@ function aggregate(sessions, period) {
731
731
  };
732
732
  }
733
733
  export async function getAnalytics(scope, period, projectSlug, sessionId, forceRefresh) {
734
- var cacheKey = scope + ":" + period + ":" + (projectSlug || "all");
734
+ let cacheKey = scope + ":" + period + ":" + (projectSlug || "all");
735
735
  if (sessionId)
736
736
  cacheKey += ":" + sessionId;
737
737
  if (!forceRefresh) {
738
- var cached = cache.get(cacheKey);
738
+ const cached = cache.get(cacheKey);
739
739
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
740
740
  return Promise.resolve(cached.data);
741
741
  }
742
- var existing = inflight.get(cacheKey);
742
+ const existing = inflight.get(cacheKey);
743
743
  if (existing)
744
744
  return existing;
745
745
  }
746
- var config = loadConfig();
747
- var cutoff = getPeriodCutoff(period);
748
- var fileRefs = [];
746
+ const config = loadConfig();
747
+ const cutoff = getPeriodCutoff(period);
748
+ const fileRefs = [];
749
749
  if (scope === "global") {
750
- for (var i = 0; i < config.projects.length; i++) {
751
- var proj = config.projects[i];
752
- var files = getSessionFilesForProject(proj.path, cutoff);
753
- for (var j = 0; j < files.length; j++) {
750
+ for (let i = 0; i < config.projects.length; i++) {
751
+ const proj = config.projects[i];
752
+ const files = getSessionFilesForProject(proj.path, cutoff);
753
+ for (let j = 0; j < files.length; j++) {
754
754
  fileRefs.push({ path: files[j].path, id: files[j].id, slug: proj.slug });
755
755
  }
756
756
  }
757
757
  }
758
758
  else if (scope === "project" && projectSlug) {
759
- var project = config.projects.find(function (p) { return p.slug === projectSlug; });
759
+ const project = config.projects.find(function (p) { return p.slug === projectSlug; });
760
760
  if (project) {
761
- var projFiles = getSessionFilesForProject(project.path, cutoff);
762
- for (var pf = 0; pf < projFiles.length; pf++) {
761
+ const projFiles = getSessionFilesForProject(project.path, cutoff);
762
+ for (let pf = 0; pf < projFiles.length; pf++) {
763
763
  fileRefs.push({ path: projFiles[pf].path, id: projFiles[pf].id, slug: projectSlug });
764
764
  }
765
765
  }
766
766
  }
767
767
  else if (scope === "session" && projectSlug && sessionId) {
768
- var sessProject = config.projects.find(function (p) { return p.slug === projectSlug; });
768
+ const sessProject = config.projects.find(function (p) { return p.slug === projectSlug; });
769
769
  if (sessProject) {
770
- var hash = projectPathToHash(sessProject.path);
771
- var filePath = join(homedir(), ".claude", "projects", hash, sessionId + ".jsonl");
770
+ const hash = projectPathToHash(sessProject.path);
771
+ const filePath = join(homedir(), ".claude", "projects", hash, sessionId + ".jsonl");
772
772
  if (existsSync(filePath)) {
773
773
  fileRefs.push({ path: filePath, id: sessionId, slug: projectSlug });
774
774
  }
775
775
  }
776
776
  }
777
- var promise = Promise.all(fileRefs.map(function (ref) {
777
+ const promise = Promise.all(fileRefs.map(function (ref) {
778
778
  return parseSessionFileAsync(ref.path, ref.id, ref.slug);
779
779
  })).then(function (results) {
780
- var sessions = results.filter(function (s) { return s !== null; });
781
- var result = aggregate(sessions, period);
780
+ const sessions = results.filter(function (s) { return s !== null; });
781
+ const result = aggregate(sessions, period);
782
782
  cache.set(cacheKey, { data: result, timestamp: Date.now() });
783
783
  inflight.delete(cacheKey);
784
784
  return result;
@@ -786,24 +786,24 @@ export async function getAnalytics(scope, period, projectSlug, sessionId, forceR
786
786
  inflight.set(cacheKey, promise);
787
787
  return promise;
788
788
  }
789
- var dailySpendCache = null;
790
- var DAILY_SPEND_CACHE_TTL = 30 * 1000;
789
+ let dailySpendCache = null;
790
+ const DAILY_SPEND_CACHE_TTL = 30 * 1000;
791
791
  export function getDailySpend() {
792
792
  if (dailySpendCache && Date.now() - dailySpendCache.timestamp < DAILY_SPEND_CACHE_TTL) {
793
793
  return dailySpendCache.value;
794
794
  }
795
- var config = loadConfig();
796
- var now = new Date();
797
- var todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
798
- var totalCost = 0;
799
- for (var i = 0; i < config.projects.length; i++) {
800
- var proj = config.projects[i];
801
- var files = getSessionFilesForProject(proj.path);
802
- for (var j = 0; j < files.length; j++) {
803
- var data = parseSessionFile(files[j].path, files[j].id, proj.slug);
795
+ const config = loadConfig();
796
+ const now = new Date();
797
+ const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
798
+ let totalCost = 0;
799
+ for (let i = 0; i < config.projects.length; i++) {
800
+ const proj = config.projects[i];
801
+ const files = getSessionFilesForProject(proj.path);
802
+ for (let j = 0; j < files.length; j++) {
803
+ const data = parseSessionFile(files[j].path, files[j].id, proj.slug);
804
804
  if (!data)
805
805
  continue;
806
- var sessionTime = data.endTime > 0 ? data.endTime : data.startTime;
806
+ const sessionTime = data.endTime > 0 ? data.endTime : data.startTime;
807
807
  if (sessionTime >= todayStart) {
808
808
  totalCost += data.cost;
809
809
  }
@@ -816,14 +816,14 @@ export function invalidateDailySpendCache() {
816
816
  dailySpendCache = null;
817
817
  }
818
818
  export async function streamAnalyticsSections(scope, period, projectSlug, sessionId, forceRefresh, onSection) {
819
- var payload = await getAnalytics(scope, period, projectSlug, sessionId, forceRefresh);
820
- var sectionNames = ["summary", "spending", "usage", "activity", "projects"];
821
- for (var si = 0; si < sectionNames.length; si++) {
822
- var name = sectionNames[si];
823
- var keys = SECTION_KEYS[name];
824
- var sectionData = {};
825
- for (var ki = 0; ki < keys.length; ki++) {
826
- var key = keys[ki];
819
+ const payload = await getAnalytics(scope, period, projectSlug, sessionId, forceRefresh);
820
+ const sectionNames = ["summary", "spending", "usage", "activity", "projects"];
821
+ for (let si = 0; si < sectionNames.length; si++) {
822
+ const name = sectionNames[si];
823
+ const keys = SECTION_KEYS[name];
824
+ const sectionData = {};
825
+ for (let ki = 0; ki < keys.length; ki++) {
826
+ const key = keys[ki];
827
827
  sectionData[key] = payload[key];
828
828
  }
829
829
  onSection(name, sectionData);