farming-code 2.2.18 → 2.2.20

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/dist/index.html CHANGED
@@ -15,7 +15,7 @@
15
15
  <link rel="apple-touch-icon" sizes="180x180" href="/farming/farming-2/app-icon-v2-180.png" />
16
16
  <link rel="manifest" href="/farming/farming-2/site.webmanifest" />
17
17
  <title>Farming 2</title>
18
- <script type="module" crossorigin src="/farming/assets/index-fp8VhOes.js"></script>
18
+ <script type="module" crossorigin src="/farming/assets/index-DuMjesL2.js"></script>
19
19
  <link rel="modulepreload" crossorigin href="/farming/assets/chunk-DECur_0Z.js">
20
20
  <link rel="modulepreload" crossorigin href="/farming/assets/preload-helper-mJowh_hw.js">
21
21
  <link rel="modulepreload" crossorigin href="/farming/assets/jsx-runtime-BthL93pI.js">
@@ -55,6 +55,9 @@ let billingRequestSequence = 0;
55
55
  let billingAbortController = null;
56
56
  let billingRefreshTimer = null;
57
57
  let billingLiveDayRefreshTimer = null;
58
+ let crtTokenRateRefreshTimer = null;
59
+ let crtTokenRateFirstLoadTimer = null;
60
+ let crtTokenRateAbortController = null;
58
61
  let billingCanvasFrame = null;
59
62
  let billingMode = 'days';
60
63
  let billingSelectedDate = '';
@@ -116,8 +119,10 @@ const CRT_AGENT_GRID_GAP = 15;
116
119
  const CRT_AGENT_GRID_PADDING = 20;
117
120
  const CRT_SEARCH_DEBOUNCE_MS = 180;
118
121
  const CRT_SEARCH_RESULT_LIMIT = 100;
119
- const CRT_BILLING_REFRESH_MS = 30_000;
122
+ const CRT_BILLING_REFRESH_MS = 15_000;
120
123
  const CRT_BILLING_LIVE_DAY_REFRESH_MS = 5_000;
124
+ const CRT_TOKEN_RATE_REFRESH_MS = 60_000;
125
+ const CRT_TOKEN_RATE_FIRST_LOAD_MS = 1_500;
121
126
  const CRT_BILLING_DAY_DETAIL_CACHE_MS = 30_000;
122
127
  const CRT_BILLING_TOTAL_ANIMATION_MS = 900;
123
128
  const CRT_BILLING_DAY_DETAIL_RETRY_MS = 750;
@@ -3159,6 +3164,7 @@ function setCrtMainView(view) {
3159
3164
  if (historySidebarItem) historySidebarItem.classList.toggle('active', crtMainView === 'history');
3160
3165
  if (searchSidebarItem) searchSidebarItem.classList.toggle('active', crtMainView === 'search');
3161
3166
  if (billingSidebarItem) billingSidebarItem.classList.toggle('active', crtMainView === 'billing');
3167
+ if (previousView !== 'agents' && crtMainView === 'agents') checkMainAgentStatus();
3162
3168
  }
3163
3169
 
3164
3170
  function getCrtSearchResults() {
@@ -3409,6 +3415,7 @@ function setupCrtSearchControls() {
3409
3415
  }
3410
3416
 
3411
3417
  function formatCrtUsageValue(value) {
3418
+ if (value === null || value === undefined || value === '') return '--';
3412
3419
  const numberValue = Number(value);
3413
3420
  if (!Number.isFinite(numberValue) || numberValue < 0) return '--';
3414
3421
  if (numberValue >= 1_000_000_000) {
@@ -4005,6 +4012,30 @@ function scrollCrtBillingSelectedDayIntoView() {
4005
4012
  else if (right > scroll.scrollLeft + scroll.clientWidth) scroll.scrollLeft = right - scroll.clientWidth;
4006
4013
  }
4007
4014
 
4015
+ function crtBillingDayArrowTargetIndex(points, index, key) {
4016
+ if (!Array.isArray(points) || index < 0 || index >= points.length) return -1;
4017
+ const firstDate = parseCrtBillingDate(points[0] && points[0].date);
4018
+ const leadingDays = firstDate ? (firstDate.getDay() + 6) % 7 : 0;
4019
+ const position = leadingDays + index;
4020
+ const weekdayRow = position % 7;
4021
+ let targetPosition = position;
4022
+ if (key === 'ArrowUp') {
4023
+ if (weekdayRow === 0) return -1;
4024
+ targetPosition -= 1;
4025
+ } else if (key === 'ArrowDown') {
4026
+ if (weekdayRow === 6) return -1;
4027
+ targetPosition += 1;
4028
+ } else if (key === 'ArrowLeft') {
4029
+ targetPosition -= 7;
4030
+ } else if (key === 'ArrowRight') {
4031
+ targetPosition += 7;
4032
+ } else {
4033
+ return -1;
4034
+ }
4035
+ const targetIndex = targetPosition - leadingDays;
4036
+ return targetIndex >= 0 && targetIndex < points.length ? targetIndex : -1;
4037
+ }
4038
+
4008
4039
  function selectCrtBillingDayByArrow(key) {
4009
4040
  if (crtMainView !== 'billing' || billingMode !== 'days') return false;
4010
4041
  const points = billingSummary && billingSummary.daily && Array.isArray(billingSummary.daily.points)
@@ -4013,8 +4044,8 @@ function selectCrtBillingDayByArrow(key) {
4013
4044
  if (points.length === 0) return false;
4014
4045
  let index = points.findIndex(point => point.date === billingSelectedDate);
4015
4046
  if (index < 0) index = points.length - 1;
4016
- const delta = key === 'ArrowLeft' ? -1 : key === 'ArrowRight' ? 1 : key === 'ArrowUp' ? -7 : 7;
4017
- const nextIndex = Math.max(0, Math.min(points.length - 1, index + delta));
4047
+ const nextIndex = crtBillingDayArrowTargetIndex(points, index, key);
4048
+ if (nextIndex < 0) return false;
4018
4049
  return selectCrtBillingDay(points[nextIndex].date, { focus: true });
4019
4050
  }
4020
4051
 
@@ -4155,11 +4186,35 @@ function setCrtBillingMode(mode) {
4155
4186
  }
4156
4187
  const status = document.getElementById('billing-status');
4157
4188
  if (status && billingSummary && !billingLoading && !billingError) {
4158
- status.textContent = billingMode === 'days' ? 'HISTORY READY' : 'SIGNAL LOCKED';
4189
+ status.textContent = crtBillingStatusText();
4159
4190
  }
4160
4191
  if (billingMode === 'live') window.requestAnimationFrame(() => drawCrtBillingScope());
4161
4192
  }
4162
4193
 
4194
+ function selectCrtBillingMode(mode) {
4195
+ setCrtBillingMode(mode);
4196
+ const selectedTab = document.getElementById(
4197
+ billingMode === 'live' ? 'billing-live-tab' : 'billing-days-tab',
4198
+ );
4199
+ if (selectedTab) setCrtNavigationSelection(selectedTab);
4200
+ if (billingMode === 'live' && !billingLoading) void loadCrtBilling();
4201
+ }
4202
+
4203
+ function crtBillingTimeline(summary = billingSummary) {
4204
+ return summary && summary.liveTimeline || summary && summary.timeline || null;
4205
+ }
4206
+
4207
+ function crtBillingStatusText(summary = billingSummary) {
4208
+ if (billingMode === 'days') return 'HISTORY READY';
4209
+ const sampledAt = Number(summary && summary.sampledAt);
4210
+ if (!Number.isFinite(sampledAt) || sampledAt <= 0) return 'LIVE';
4211
+ const sampled = new Date(sampledAt);
4212
+ const time = [sampled.getHours(), sampled.getMinutes(), sampled.getSeconds()]
4213
+ .map(value => String(value).padStart(2, '0'))
4214
+ .join(':');
4215
+ return `LIVE ${time}`;
4216
+ }
4217
+
4163
4218
  function formatCrtBillingWindow(windowMinutes) {
4164
4219
  const minutes = Number(windowMinutes);
4165
4220
  if (!Number.isFinite(minutes) || minutes <= 0) return 'WINDOW';
@@ -4169,6 +4224,20 @@ function formatCrtBillingWindow(windowMinutes) {
4169
4224
  return `${Math.round(minutes)}M`;
4170
4225
  }
4171
4226
 
4227
+ function crtBillingTimelineLabels(timeline) {
4228
+ const windowMinutes = Number(timeline && timeline.windowMs) / 60_000;
4229
+ const windowLabel = formatCrtBillingWindow(windowMinutes);
4230
+ const midpointLabel = formatCrtBillingWindow(windowMinutes / 2);
4231
+ return {
4232
+ integral: `TOKENS · ${windowLabel}`,
4233
+ peak: `TOK/MIN · ${windowLabel}`,
4234
+ title: `TOKEN BURN // ${windowLabel}`,
4235
+ ariaLabel: `Token burn rate over the last ${windowLabel}`,
4236
+ start: `-${windowLabel}`,
4237
+ midpoint: `-${midpointLabel}`,
4238
+ };
4239
+ }
4240
+
4172
4241
  function formatCrtBillingReset(resetsAt, now = Date.now()) {
4173
4242
  const timestamp = Number(resetsAt);
4174
4243
  if (!Number.isFinite(timestamp) || timestamp <= 0) return 'RESET --';
@@ -4180,10 +4249,30 @@ function formatCrtBillingReset(resetsAt, now = Date.now()) {
4180
4249
 
4181
4250
  function crtBillingCurrentRate(summary = billingSummary) {
4182
4251
  const providers = summary && Array.isArray(summary.providers) ? summary.providers : [];
4183
- return providers.reduce((total, provider) => {
4184
- const rate = Number(provider && provider.tokenUsage && provider.tokenUsage.tokensPerMinute);
4185
- return total + (Number.isFinite(rate) ? Math.max(0, rate) : 0);
4186
- }, 0);
4252
+ let total = 0;
4253
+ let hasRate = false;
4254
+ providers.forEach((provider) => {
4255
+ const tokenUsage = provider && provider.tokenUsage;
4256
+ const rate = tokenUsage && tokenUsage.tokensPerMinute;
4257
+ if (
4258
+ tokenUsage
4259
+ && tokenUsage.available !== false
4260
+ && typeof rate === 'number'
4261
+ && Number.isFinite(rate)
4262
+ && rate >= 0
4263
+ ) {
4264
+ total += rate;
4265
+ hasRate = true;
4266
+ }
4267
+ });
4268
+ return hasRate ? total : null;
4269
+ }
4270
+
4271
+ function renderCrtTopBarTokenRate(summary = billingSummary) {
4272
+ const tokensPerMinute = document.getElementById('tokens-per-minute');
4273
+ if (tokensPerMinute) {
4274
+ tokensPerMinute.textContent = formatCrtTokenRate(crtBillingCurrentRate(summary));
4275
+ }
4187
4276
  }
4188
4277
 
4189
4278
  function appendCrtBillingMessage(container, text, isError = false) {
@@ -4286,7 +4375,7 @@ function drawCrtBillingScope(summary = billingSummary) {
4286
4375
  context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
4287
4376
  context.clearRect(0, 0, rect.width, rect.height);
4288
4377
 
4289
- const timeline = summary && summary.timeline;
4378
+ const timeline = crtBillingTimeline(summary);
4290
4379
  const points = timeline && Array.isArray(timeline.points) ? timeline.points : [];
4291
4380
  const values = points.map(point => Math.max(0, Number(point.tokensPerMinute) || 0));
4292
4381
  const providerNames = points.length > 0 ? Object.keys(points[0].providers || {}) : [];
@@ -4364,7 +4453,7 @@ function renderCrtBilling() {
4364
4453
  const status = document.getElementById('billing-status');
4365
4454
  const refresh = document.getElementById('billing-refresh');
4366
4455
  const empty = document.getElementById('billing-scope-empty');
4367
- const timeline = billingSummary && billingSummary.timeline;
4456
+ const timeline = crtBillingTimeline();
4368
4457
  const hasSignal = Boolean(timeline && Number(timeline.totalTokens) > 0);
4369
4458
  if (status) {
4370
4459
  status.classList.toggle('is-busy', billingLoading);
@@ -4374,7 +4463,7 @@ function renderCrtBilling() {
4374
4463
  : billingLoading
4375
4464
  ? (billingSummary ? 'REFRESHING' : 'SCANNING LOGS')
4376
4465
  : billingSummary
4377
- ? (billingMode === 'days' ? 'HISTORY READY' : 'SIGNAL LOCKED')
4466
+ ? crtBillingStatusText()
4378
4467
  : 'STANDBY';
4379
4468
  }
4380
4469
  if (refresh) refresh.disabled = billingLoading;
@@ -4384,11 +4473,24 @@ function renderCrtBilling() {
4384
4473
  const peakRate = document.getElementById('billing-peak-rate');
4385
4474
  const dutyCycle = document.getElementById('billing-duty-cycle');
4386
4475
  const scopeScale = document.getElementById('billing-scope-scale');
4476
+ const windowLabel = document.getElementById('billing-window-label');
4477
+ const peakLabel = document.getElementById('billing-peak-label');
4478
+ const scopeTitle = document.getElementById('billing-scope-title');
4479
+ const scope = document.getElementById('billing-scope');
4480
+ const scopeStart = document.getElementById('billing-scope-start');
4481
+ const scopeMidpoint = document.getElementById('billing-scope-midpoint');
4482
+ const timelineLabels = crtBillingTimelineLabels(timeline);
4387
4483
  if (currentRate) currentRate.textContent = formatCrtUsageValue(crtBillingCurrentRate());
4388
4484
  if (windowTotal) windowTotal.textContent = formatCrtUsageValue(timeline && timeline.totalTokens);
4389
4485
  if (peakRate) peakRate.textContent = formatCrtUsageValue(timeline && timeline.peakTokensPerMinute);
4390
4486
  if (dutyCycle) dutyCycle.textContent = timeline ? `${timeline.activeBucketCount}/${timeline.bucketCount}` : '--';
4391
4487
  if (scopeScale) scopeScale.textContent = `${formatCrtUsageValue(timeline && timeline.peakTokensPerMinute)} TOK/MIN PEAK`;
4488
+ if (windowLabel) windowLabel.textContent = timelineLabels.integral;
4489
+ if (peakLabel) peakLabel.textContent = timelineLabels.peak;
4490
+ if (scopeTitle) scopeTitle.textContent = timelineLabels.title;
4491
+ if (scope) scope.setAttribute('aria-label', timelineLabels.ariaLabel);
4492
+ if (scopeStart) scopeStart.textContent = timelineLabels.start;
4493
+ if (scopeMidpoint) scopeMidpoint.textContent = timelineLabels.midpoint;
4392
4494
  if (empty) {
4393
4495
  empty.classList.toggle('hidden', hasSignal);
4394
4496
  empty.textContent = billingError ? 'SIGNAL LOST' : billingLoading ? 'ACQUIRING SIGNAL' : 'NO TOKEN SIGNAL';
@@ -4408,13 +4510,18 @@ async function loadCrtBilling({ fresh = false } = {}) {
4408
4510
  billingRequestSequence += 1;
4409
4511
  const requestSequence = billingRequestSequence;
4410
4512
  if (billingAbortController) billingAbortController.abort();
4513
+ if (crtTokenRateAbortController) {
4514
+ crtTokenRateAbortController.abort();
4515
+ crtTokenRateAbortController = null;
4516
+ }
4411
4517
  const controller = new window.AbortController();
4412
4518
  billingAbortController = controller;
4413
4519
  billingLoading = true;
4414
4520
  billingError = '';
4415
4521
  renderCrtBilling();
4416
4522
  try {
4417
- const response = await fetch(farmingApiPath(`/usage${fresh ? '?fresh=1' : ''}`), {
4523
+ const query = fresh ? '?fresh=1' : billingMode === 'live' ? '?live=1' : '';
4524
+ const response = await fetch(farmingApiPath(`/usage${query}`), {
4418
4525
  signal: controller.signal,
4419
4526
  cache: 'no-store',
4420
4527
  });
@@ -4422,6 +4529,7 @@ async function loadCrtBilling({ fresh = false } = {}) {
4422
4529
  if (!response.ok || !data || !data.usage) throw new Error(data && data.error ? data.error : `Usage request failed (${response.status})`);
4423
4530
  if (requestSequence !== billingRequestSequence) return;
4424
4531
  billingSummary = data.usage;
4532
+ renderCrtTopBarTokenRate();
4425
4533
  } catch (error) {
4426
4534
  if (controller.signal.aborted || requestSequence !== billingRequestSequence) return;
4427
4535
  billingError = error instanceof Error ? error.message : 'Failed to load token telemetry';
@@ -4440,6 +4548,58 @@ async function loadCrtBilling({ fresh = false } = {}) {
4440
4548
  }
4441
4549
  }
4442
4550
 
4551
+ async function loadCrtTopBarTokenRate() {
4552
+ if (billingLoading || crtTokenRateAbortController) return;
4553
+ const controller = new window.AbortController();
4554
+ crtTokenRateAbortController = controller;
4555
+ try {
4556
+ const response = await fetch(farmingApiPath('/usage'), {
4557
+ signal: controller.signal,
4558
+ cache: 'no-store',
4559
+ });
4560
+ const data = await response.json().catch(() => null);
4561
+ if (controller.signal.aborted) return;
4562
+ if (!response.ok || !data || !data.usage) {
4563
+ throw new Error(data && data.error ? data.error : `Usage request failed (${response.status})`);
4564
+ }
4565
+ renderCrtTopBarTokenRate(data.usage);
4566
+ } catch {
4567
+ if (!controller.signal.aborted) renderCrtTopBarTokenRate(null);
4568
+ } finally {
4569
+ if (crtTokenRateAbortController === controller) crtTokenRateAbortController = null;
4570
+ }
4571
+ }
4572
+
4573
+ function stopCrtTopBarTokenRateRefresh({ abort = false } = {}) {
4574
+ if (crtTokenRateFirstLoadTimer !== null) {
4575
+ clearTimeout(crtTokenRateFirstLoadTimer);
4576
+ crtTokenRateFirstLoadTimer = null;
4577
+ }
4578
+ if (crtTokenRateRefreshTimer !== null) {
4579
+ clearInterval(crtTokenRateRefreshTimer);
4580
+ crtTokenRateRefreshTimer = null;
4581
+ }
4582
+ if (abort && crtTokenRateAbortController) {
4583
+ crtTokenRateAbortController.abort();
4584
+ crtTokenRateAbortController = null;
4585
+ }
4586
+ }
4587
+
4588
+ function startCrtTopBarTokenRateRefresh({ immediate = false } = {}) {
4589
+ stopCrtTopBarTokenRateRefresh();
4590
+ if (immediate) {
4591
+ void loadCrtTopBarTokenRate();
4592
+ } else {
4593
+ crtTokenRateFirstLoadTimer = setTimeout(() => {
4594
+ crtTokenRateFirstLoadTimer = null;
4595
+ if (document.visibilityState !== 'hidden') void loadCrtTopBarTokenRate();
4596
+ }, CRT_TOKEN_RATE_FIRST_LOAD_MS);
4597
+ }
4598
+ crtTokenRateRefreshTimer = setInterval(() => {
4599
+ if (document.visibilityState !== 'hidden') void loadCrtTopBarTokenRate();
4600
+ }, CRT_TOKEN_RATE_REFRESH_MS);
4601
+ }
4602
+
4443
4603
  function stopCrtBillingRefresh({ abort = false } = {}) {
4444
4604
  if (billingRefreshTimer !== null) {
4445
4605
  clearInterval(billingRefreshTimer);
@@ -5444,7 +5604,7 @@ function connect() {
5444
5604
  else scheduleCrtPreviewCardRender(agent);
5445
5605
  }
5446
5606
  } else if (data.type === 'system-stats') {
5447
- updateSystemStats(data.stats, data.uptime, data.usageRate);
5607
+ updateSystemStats(data.stats, data.uptime);
5448
5608
  } else if (data.type === 'error') {
5449
5609
  waitingForAgent = false;
5450
5610
  if (workspaceDirectoryPrompt?.kind === 'creating') {
@@ -5491,6 +5651,7 @@ function connect() {
5491
5651
  function suspendCrtPageConnection() {
5492
5652
  document.body.classList.add('page-hidden');
5493
5653
  stopCrtBillingRefresh({ abort: true });
5654
+ stopCrtTopBarTokenRateRefresh({ abort: true });
5494
5655
  if (wsReconnectTimer) {
5495
5656
  clearTimeout(wsReconnectTimer);
5496
5657
  wsReconnectTimer = null;
@@ -5504,6 +5665,7 @@ function resumeCrtPageConnection() {
5504
5665
  if (document.visibilityState === 'hidden') return;
5505
5666
  document.body.classList.remove('page-hidden');
5506
5667
  connect();
5668
+ startCrtTopBarTokenRateRefresh({ immediate: true });
5507
5669
  if (crtMainView === 'billing') {
5508
5670
  startCrtBillingRefresh();
5509
5671
  void loadCrtBilling();
@@ -5513,15 +5675,33 @@ function resumeCrtPageConnection() {
5513
5675
  function checkMainAgentStatus() {
5514
5676
  if (!state) return;
5515
5677
 
5516
- const mainAgent = state.mainAgentId
5517
- ? state.agents.find(a => a.id === state.mainAgentId)
5518
- : null;
5519
-
5520
- if (!state.mainAgentId || !isCrtLiveAgent(mainAgent)) {
5678
+ const inputDialog = document.getElementById('input-dialog');
5679
+ const action = getCrtMainAgentDialogAction({
5680
+ currentState: state,
5681
+ mainView: crtMainView,
5682
+ dialogActive: Boolean(inputDialog && inputDialog.classList.contains('active')),
5683
+ pendingMainAgent: pendingMainAgentLaunch,
5684
+ });
5685
+ if (action === 'hide') {
5686
+ hideInputDialog();
5687
+ } else if (action === 'show') {
5521
5688
  showInputDialog();
5522
5689
  }
5523
5690
  }
5524
5691
 
5692
+ function getCrtMainAgentDialogAction({
5693
+ currentState,
5694
+ mainView = 'agents',
5695
+ dialogActive = false,
5696
+ pendingMainAgent = false,
5697
+ } = {}) {
5698
+ if (!needsMainAgent(currentState)) {
5699
+ return dialogActive && pendingMainAgent ? 'hide' : 'none';
5700
+ }
5701
+ if (mainView !== 'agents' || dialogActive) return 'none';
5702
+ return 'show';
5703
+ }
5704
+
5525
5705
  function formatSystemClock(timestamp, timeZone) {
5526
5706
  if (!Number.isFinite(timestamp)) return '--';
5527
5707
  try {
@@ -5541,21 +5721,22 @@ function formatSystemClock(timestamp, timeZone) {
5541
5721
  }
5542
5722
 
5543
5723
  function formatCrtTokenRate(value) {
5724
+ if (value === null || value === undefined || value === '') return '--';
5544
5725
  const rate = Number(value);
5545
5726
  if (!Number.isFinite(rate) || rate < 0) return '--';
5546
5727
  const rounded = rate < 10 ? Math.round(rate * 10) / 10 : Math.round(rate);
5547
5728
  if (rounded >= 1_000_000) {
5548
5729
  const compact = rounded / 1_000_000;
5549
- return `~${compact >= 10 ? Math.round(compact) : Math.round(compact * 10) / 10}M`;
5730
+ return `${compact >= 10 ? Math.round(compact) : Math.round(compact * 10) / 10}M`;
5550
5731
  }
5551
5732
  if (rounded >= 1_000) {
5552
5733
  const compact = rounded / 1_000;
5553
- return `~${compact >= 10 ? Math.round(compact) : Math.round(compact * 10) / 10}K`;
5734
+ return `${compact >= 10 ? Math.round(compact) : Math.round(compact * 10) / 10}K`;
5554
5735
  }
5555
- return `~${rounded}`;
5736
+ return String(rounded);
5556
5737
  }
5557
5738
 
5558
- function updateSystemStats(stats, uptime, usageRate) {
5739
+ function updateSystemStats(stats, uptime) {
5559
5740
  if (stats.cpu !== undefined) {
5560
5741
  document.getElementById('cpu-usage').textContent = stats.cpu;
5561
5742
  }
@@ -5572,11 +5753,6 @@ function updateSystemStats(stats, uptime, usageRate) {
5572
5753
  document.getElementById('system-time').textContent = formatSystemClock(stats.timestamp, stats.timeZone);
5573
5754
  }
5574
5755
 
5575
- const tokensPerMinute = document.getElementById('tokens-per-minute');
5576
- if (tokensPerMinute) {
5577
- tokensPerMinute.textContent = formatCrtTokenRate(usageRate && usageRate.estimatedTokensPerMinute);
5578
- }
5579
-
5580
5756
  if (uptime !== undefined) {
5581
5757
  const hours = Math.floor(uptime / 3600);
5582
5758
  const minutes = Math.floor((uptime % 3600) / 60);
@@ -6130,10 +6306,45 @@ function flushStructuredComposerFollowUp(agent) {
6130
6306
  setTimeout(() => void refreshStructuredSession(agent.id, true), 160);
6131
6307
  }
6132
6308
 
6309
+ function structuredContextUsage(session) {
6310
+ const usage = session && session.usage;
6311
+ const usedTokens = usage && usage.used;
6312
+ const limitTokens = usage && usage.size;
6313
+ if (
6314
+ typeof usedTokens !== 'number'
6315
+ || !Number.isFinite(usedTokens)
6316
+ || usedTokens < 0
6317
+ || typeof limitTokens !== 'number'
6318
+ || !Number.isFinite(limitTokens)
6319
+ || limitTokens <= 0
6320
+ ) return null;
6321
+ const percentUsed = Math.max(0, Math.min(100, Math.round(usedTokens / limitTokens * 100)));
6322
+ return {
6323
+ usedTokens,
6324
+ limitTokens,
6325
+ percentUsed,
6326
+ percentLeft: 100 - percentUsed,
6327
+ };
6328
+ }
6329
+
6133
6330
  function formatStructuredUsage(session) {
6134
- const tokens = Number(session && session.usage && session.usage.totalTokens);
6135
- if (!Number.isFinite(tokens) || tokens <= 0) return '';
6136
- return `${Math.round(tokens / 1000)}K TOK`;
6331
+ const contextUsage = structuredContextUsage(session);
6332
+ if (!contextUsage) return '';
6333
+ return `${formatCrtUsageValue(contextUsage.usedTokens)} / ${formatCrtUsageValue(contextUsage.limitTokens)} TOK`;
6334
+ }
6335
+
6336
+ function updateStructuredUsageDisplay(element, session) {
6337
+ if (!element) return;
6338
+ const contextUsage = structuredContextUsage(session);
6339
+ element.textContent = formatStructuredUsage(session);
6340
+ if (!contextUsage) {
6341
+ element.removeAttribute('title');
6342
+ element.removeAttribute('aria-label');
6343
+ return;
6344
+ }
6345
+ const label = `Context window: ${contextUsage.percentUsed}% used (${contextUsage.percentLeft}% left)`;
6346
+ element.title = label;
6347
+ element.setAttribute('aria-label', label);
6137
6348
  }
6138
6349
 
6139
6350
  function structuredSelectOptions(option) {
@@ -6187,7 +6398,7 @@ function resetStructuredSessionControls() {
6187
6398
  if (commandButton) commandButton.hidden = true;
6188
6399
  if (modeButton) modeButton.hidden = true;
6189
6400
  if (configButton) configButton.hidden = true;
6190
- if (usage) usage.textContent = '';
6401
+ updateStructuredUsageDisplay(usage, null);
6191
6402
  }
6192
6403
 
6193
6404
  function setStructuredComposerMenu(menu, { focusFirst = false, opener = null } = {}) {
@@ -6429,7 +6640,7 @@ function renderStructuredSessionControls() {
6429
6640
  const currentMode = modes.find((mode) => mode.id === (session && (session.currentModeId || (session.modes && session.modes.currentModeId))));
6430
6641
  modeButton.textContent = `[MODE ${currentMode ? currentMode.name : ''}]`;
6431
6642
  configButton.textContent = `[${currentStructuredConfigLabel(session)}]`;
6432
- usage.textContent = formatStructuredUsage(session);
6643
+ updateStructuredUsageDisplay(usage, session);
6433
6644
  renderStructuredComposerMenu();
6434
6645
  }
6435
6646
 
@@ -7876,13 +8087,14 @@ if (typeof document !== 'undefined') {
7876
8087
 
7877
8088
  if (
7878
8089
  crtMainView === 'billing'
8090
+ && billingMode === 'days'
7879
8091
  && !sessionActive
7880
8092
  && !e.ctrlKey
7881
8093
  && !e.metaKey
7882
8094
  && !e.altKey
7883
8095
  && navigationArrow
7884
- && selectCrtBillingDayByArrow(e.key)
7885
8096
  ) {
8097
+ selectCrtBillingDayByArrow(e.key);
7886
8098
  e.preventDefault();
7887
8099
  return;
7888
8100
  }
@@ -8076,13 +8288,13 @@ if (typeof document !== 'undefined') {
8076
8288
  }
8077
8289
 
8078
8290
  if ((e.key === 'd' || e.key === 'D') && crtMainView === 'billing') {
8079
- setCrtBillingMode('days');
8291
+ selectCrtBillingMode('days');
8080
8292
  e.preventDefault();
8081
8293
  return;
8082
8294
  }
8083
8295
 
8084
8296
  if ((e.key === 'l' || e.key === 'L') && crtMainView === 'billing') {
8085
- setCrtBillingMode('live');
8297
+ selectCrtBillingMode('live');
8086
8298
  e.preventDefault();
8087
8299
  return;
8088
8300
  }
@@ -8253,6 +8465,10 @@ if (typeof module !== 'undefined' && module.exports) {
8253
8465
  crtAgentSessionKey,
8254
8466
  crtResumedSessionFromSource,
8255
8467
  crtDashboardStateSignature,
8468
+ crtBillingDayArrowTargetIndex,
8469
+ crtBillingTimelineLabels,
8470
+ crtBillingTimeline,
8471
+ crtBillingCurrentRate,
8256
8472
  findDefaultNewAgentIndex,
8257
8473
  findDirectionalNavigationIndex,
8258
8474
  isBrowserShortcut,
@@ -8269,11 +8485,14 @@ if (typeof module !== 'undefined' && module.exports) {
8269
8485
  formatCrtTokenRate,
8270
8486
  formatCrtHistoryAge,
8271
8487
  formatCrtCompactTotalValue,
8488
+ formatCrtUsageValue,
8489
+ formatStructuredUsage,
8272
8490
  getCrtHistoryPage,
8273
8491
  getCrtAgentPage,
8274
8492
  getCrtAgentVerticalPageTarget,
8275
8493
  getCrtLiveAgents,
8276
8494
  getCrtRegularAgents,
8495
+ getCrtMainAgentDialogAction,
8277
8496
  getCrtAgentRemovalFallback,
8278
8497
  getAgentDisplayText,
8279
8498
  getCrtPreviewCellStyle,
@@ -8290,6 +8509,8 @@ if (typeof module !== 'undefined' && module.exports) {
8290
8509
  hasCrtStructuredLocalEscapeAction,
8291
8510
  resolveCrtSessionKeyboardCommand,
8292
8511
  structuredComposerAction,
8512
+ structuredContextUsage,
8513
+ updateStructuredUsageDisplay,
8293
8514
  structuredTranscriptTurns,
8294
8515
  crtStructuredPreviewMessageLines,
8295
8516
  getCrtBrandPaneKey,
@@ -8316,5 +8537,6 @@ if (typeof module !== 'undefined' && module.exports) {
8316
8537
  });
8317
8538
  window.addEventListener('pagehide', suspendCrtPageConnection);
8318
8539
  window.addEventListener('pageshow', resumeCrtPageConnection);
8540
+ startCrtTopBarTokenRateRefresh();
8319
8541
  connect();
8320
8542
  }