maxpool 1.24.27 → 1.24.28

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.24.27",
3
+ "version": "1.24.28",
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",
@@ -327,6 +327,10 @@ const PERSISTED_QUOTA_FIELDS = [
327
327
  'providerQuotaSource', 'lastProbeOkAt',
328
328
  ];
329
329
 
330
+ const PROVIDER_429_MIN_BENCH_SEC = 60;
331
+ const PROVIDER_429_MAX_BENCH_SEC = 15 * 60;
332
+ const PROVIDER_429_STREAK_FORGET_MS = 60 * 60 * 1000;
333
+
330
334
  function clampRetryAfterSeconds(value) {
331
335
  const n = Number(value);
332
336
  if (!Number.isFinite(n)) return 60;
@@ -845,6 +849,7 @@ export class AccountManager {
845
849
  if (outcome.success) {
846
850
  account.completedRequests++;
847
851
  account.consecutiveFailures = 0;
852
+ account.providerRateLimitStreak = 0;
848
853
  account.lastStatus = outcome.status || account.lastStatus;
849
854
  account.lastResponseMs = Date.now() - lease.startedAt;
850
855
  if (account.provisionalUpstreamFingerprint) {
@@ -3927,8 +3932,31 @@ export class AccountManager {
3927
3932
  return;
3928
3933
  }
3929
3934
 
3935
+ // Provider "slow down" backoff (2026-10-21): z.ai answers a GLM 429 with a 3-12s
3936
+ // hint, and honouring it re-selected the account seconds later — ~200 refusals on
3937
+ // max+1 over 2026-10-16/17, then a z.ai usage-risk notice. Bench a provider 429
3938
+ // for at least 60s, doubling per back-to-back 429 up to 15 min; a success resets
3939
+ // the streak (releaseAccount). Claude accounts keep the exact retry-after.
3940
+ // A parallel 429 landing while the account is already benched is the SAME refusal
3941
+ // burst — it keeps the bench and does not escalate (red-team 2026-10-21: up to 50
3942
+ // in-flight requests per account turned one burst into the 15-min cap). The streak
3943
+ // also forgets after an hour with no refusals.
3944
+ let benchSeconds = retryAfter;
3945
+ if (account.type === 'provider' && (options.status || 429) === 429) {
3946
+ const now = Date.now();
3947
+ if (account.status === 'throttled' && account.rateLimitedUntil > now) {
3948
+ benchSeconds = Math.max(retryAfter, Math.ceil((account.rateLimitedUntil - now) / 1000));
3949
+ } else {
3950
+ if (now - (account.providerRateLimitAt || 0) > PROVIDER_429_STREAK_FORGET_MS) account.providerRateLimitStreak = 0;
3951
+ const streak = account.providerRateLimitStreak || 0;
3952
+ benchSeconds = Math.max(retryAfter, Math.min(PROVIDER_429_MAX_BENCH_SEC, PROVIDER_429_MIN_BENCH_SEC * 2 ** streak));
3953
+ account.providerRateLimitStreak = streak + 1;
3954
+ }
3955
+ account.providerRateLimitAt = now;
3956
+ }
3957
+
3930
3958
  account.status = 'throttled';
3931
- account.rateLimitedUntil = Date.now() + (retryAfter * 1000);
3959
+ account.rateLimitedUntil = Date.now() + (benchSeconds * 1000);
3932
3960
  account.lastStatus = options.status || 429;
3933
3961
  account.lastError = 'rate_limited';
3934
3962
  account.lastErrorAt = Date.now();
@@ -3937,7 +3965,7 @@ export class AccountManager {
3937
3965
  account.failedRequests++;
3938
3966
  account.consecutiveFailures++;
3939
3967
  }
3940
- console.log(`[Maxpool] Account "${account.name}" rate limited for ${retryAfter}s`);
3968
+ console.log(`[Maxpool] Account "${account.name}" rate limited for ${benchSeconds}s${benchSeconds > retryAfter ? ` (provider asked ${retryAfter}s; backing off)` : ''}`);
3941
3969
  }
3942
3970
 
3943
3971
  markAuthFailed(accountIndex, status = 403, reason = 'auth_failed') {