maxpool 1.19.8 → 1.20.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maxpool",
3
- "version": "1.19.8",
3
+ "version": "1.20.0",
4
4
  "description": "Multi-account Claude Code proxy with adaptive, rate-aware load balancing across Claude accounts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -147,6 +147,13 @@ const DEFAULT_SCHEDULER = {
147
147
  capPenaltyWeight: 10, // steep penalty per unit of in-flight depth past D (throttle safety floor)
148
148
  paceCostWeight: 1.5, // soft de-preference of accounts burning ahead of pace (was the ×6 term)
149
149
  utilizationWeight: 3, // RAW utilization cost — drives load balancing in the mid-range
150
+ // WEEKLY-AWARE SCORING (2026-09-16): when true, _rawUtilization also folds in the
151
+ // WEEKLY utilization (unified7d / providerWk), not just the 5h session. Before this,
152
+ // a Claude account at 89% weekly with a freshly-reset 5h window scored as CHEAP —
153
+ // weekly-burning accounts kept winning the lease all day (backtest: 62%→35% mean
154
+ // weekly burn once weekly-aware). Boolean; default ON was chosen because the
155
+ // pre-flag behavior is the bug this fixes.
156
+ weeklyAwareScoring: true,
150
157
  scarcityWeight: 6, // legacy; superseded by paceCostWeight (kept so old configs don't error)
151
158
  // Reserve-account OVERFLOW model. A weekly-RESERVE account (util 0.85-0.95) used to
152
159
  // sit idle behind a healthy-only first pass; now it's eligible in the first pass but
@@ -2692,7 +2699,7 @@ export class AccountManager {
2692
2699
  * _accountScarcity but WITHOUT the elapsed-fraction discount. This is the signal
2693
2700
  * the load balancer needs: an account at 80% is more expensive than one at 10%,
2694
2701
  * full stop. */
2695
- _rawUtilization(account) {
2702
+ _rawUtilization(account, now = Date.now()) {
2696
2703
  const q = account?.quota;
2697
2704
  if (!q) return 0;
2698
2705
  // SESSION windows use raw utilization — headroom is consumed immediately and an
@@ -2708,6 +2715,24 @@ export class AccountManager {
2708
2715
  if (q.tokensLimit != null && q.tokensLimit > 0 && q.tokensRemaining != null) {
2709
2716
  util = Math.max(util, 1 - q.tokensRemaining / q.tokensLimit);
2710
2717
  }
2718
+ // WEEKLY-AWARE (2026-09-16): fold the WEEKLY number in too, behind its own
2719
+ // flag — via _windowScarcity (reset-aware), NEVER raw. v1 of this block used
2720
+ // raw max(), which fixed the 89%-weekly-wins-all-day bug but broke the
2721
+ // near-reset contracts that predate it: the use-it-or-lose-it pin, the
2722
+ // preReset drain (X2/X5), and S10's flag-off parity. Pace-adjusted, a
2723
+ // 60%-weekly account mid-window adds (0.60 − elapsedFrac) here at weight 3
2724
+ // ON TOP of the paceCost's weight-1.5 copy — doubling the weekly steering
2725
+ // signal (the actual fix) while capacity dying at reset stays free (the
2726
+ // invariant the older tests pin). Unknown/absent reset → face value, same as
2727
+ // _accountScarcity. Turn the flag off to restore session-only exactly.
2728
+ if (this.scheduler.weeklyAwareScoring !== false) {
2729
+ if (q.unified7d != null) {
2730
+ util = Math.max(util, this._windowScarcity(q.unified7d, q.unified7dReset, WEEK_MS, now));
2731
+ }
2732
+ if (q.providerWk != null) {
2733
+ util = Math.max(util, this._windowScarcity(q.providerWk, q.providerWkReset, WEEK_MS, now));
2734
+ }
2735
+ }
2711
2736
  return util;
2712
2737
  }
2713
2738
 
package/src/tui.js CHANGED
@@ -977,6 +977,11 @@ export class TUI {
977
977
  // mode — under balance/prefer-* the mode itself controls eligibility. Still
978
978
  // safe to set (it'll apply if you switch back to sticky).
979
979
  this._cycleProviderClaudeFallback(k === 'g' ? 'zai' : 'kimi');
980
+ } else if (k === 'w' || k === 'W') {
981
+ // WEEKLY-AWARE SCORING (2026-09-16): fold weekly utilization into the routing
982
+ // score, not just the 5h session. Without it, an account at 89% weekly with a
983
+ // fresh session window scores as cheap and keeps winning all day.
984
+ this._toggleWeeklyAware();
980
985
  } else if (k === 'esc' || k === 'q') {
981
986
  this.mode = 'normal';
982
987
  }
@@ -1066,6 +1071,22 @@ export class TUI {
1066
1071
  : `Peak cap: bench a GLM account once it passes ${Math.round(next * 100)}% of its weekly quota`);
1067
1072
  }
1068
1073
 
1074
+ /** Toggle weekly-aware routing for the whole fleet. One scheduler flag — when OFF the
1075
+ * score sees only the 5h session (the pre-2026-09-16 behavior); when ON the weekly
1076
+ * number is folded in, so a nearly-exhausted account loses to a fresh one at ALL
1077
+ * hours, not just after its session window resets. */
1078
+ async _toggleWeeklyAware() {
1079
+ const next = this.am.scheduler.weeklyAwareScoring === false;
1080
+ this.am.scheduler.weeklyAwareScoring = next;
1081
+ const sched = { ...(this.config.scheduler || {}) };
1082
+ sched.weeklyAwareScoring = next;
1083
+ this.config.scheduler = sched;
1084
+ await this.saveConfig(this.config);
1085
+ this._addLog(next
1086
+ ? 'Weekly-aware routing: ON — accounts near their weekly limit rank last, all day'
1087
+ : 'Weekly-aware routing: OFF — score sees only the 5h session window again');
1088
+ }
1089
+
1069
1090
  async _cycleRoutingMode() {
1070
1091
  const modes = TUI.ROUTING_MODES;
1071
1092
  const cur = this.am.scheduler?.routingMode || 'sticky';
@@ -2369,7 +2390,8 @@ export class TUI {
2369
2390
  const now = st?.inPeak ? red(' NOW') : '';
2370
2391
  const dep = ps.depreference ? yellow('GLM last') : cyan('normal');
2371
2392
  const cap = ps.cap >= 1 ? 'off' : ps.cap === 0 ? 'never' : `${Math.round(ps.cap * 100)}%`;
2372
- peakPart = ` ${dim('│')} ${bold(' d ')}Peak${now}: ${dep} ${bold(' c ')}cap ${cyan(cap)}`;
2393
+ const wk = this.am.scheduler.weeklyAwareScoring === false ? yellow('5h-only') : cyan('weekly');
2394
+ peakPart = ` ${dim('│')} ${bold(' d ')}Peak${now}: ${dep} ${bold(' c ')}cap ${cyan(cap)} ${bold(' w ')}score:${wk}`;
2373
2395
  }
2374
2396
  return ` ${bold('f')} Routing: ${cyan(mode.label)} ↻${provPart}${peakPart} ${bold('p')} Preference ${bold('Esc')} Back`;
2375
2397
  }