maxpool 1.24.29 → 1.24.31

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.29",
3
+ "version": "1.24.31",
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",
package/src/prober.js CHANGED
@@ -219,16 +219,23 @@ export class Prober {
219
219
  * the backoff; the idempotent requestId keeps any overlapping attempt single-spend. */
220
220
  async _redeemZaiCard(account, act) {
221
221
  try {
222
- const key = `${account.index}:${act.card.recordId}`;
222
+ const key = `${account.name}:${act.card.recordId}`; // name, not index: index shifts on account removal
223
223
  const now = Date.now();
224
224
  const until = this._cardRetryUntil?.get(key);
225
225
  if (until && until > now) return; // still backing off
226
226
  const q = account.quota || {};
227
227
  this.log(`[reset-card] ${account.name}: redeeming ${act.resetType} card ${act.card.recordId} (${act.reason}; ses=${q.providerSes ?? '?'} wk=${q.providerWk ?? '?'})`);
228
- const r = await this._withTimeout(redeemResetCard(account.credential, act.card.recordId, act.resetType, { grantType: act.card.grantType }));
228
+ let r = await this._withTimeout(redeemResetCard(account.credential, act.card.recordId, act.resetType, { grantType: act.card.grantType, accountKey: account.name }));
229
+ // grantType-less listing + "unavailable" is the exact 2026-10-22 outage shape; GLOBAL
230
+ // is the only grantType z.ai has ever issued and a wrong one answers 400 no-spend
231
+ // (measured, src/zai-reset-cards.js header) — so one fallback retry is safe.
232
+ if (!r?.ok && !act.card.grantType && /unavailable/i.test(r?.error || '')) {
233
+ r = await this._withTimeout(redeemResetCard(account.credential, act.card.recordId, act.resetType, { grantType: 'GLOBAL', accountKey: account.name }));
234
+ }
229
235
  if (r?.ok) {
230
236
  if (!this._cardRetryUntil) this._cardRetryUntil = new Map();
231
237
  this._cardRetryUntil.set(key, 0); // success clears any backoff
238
+ this._cardTries?.set(key, 0); // ...and the failure count (red-team 2026-10-23)
232
239
  this.log(`[reset-card] ${account.name}: card ${act.card.recordId} redeemed`);
233
240
  if (this.am.applyResetCards) {
234
241
  const fresh = await this._withTimeout(listResetCards(account.credential));
@@ -34,6 +34,13 @@
34
34
  * R5 PACE — at most ONE redeem per account per evaluation. The caller invokes the
35
35
  * policy each poll cycle; the deterministic z.ai requestId makes an accidental
36
36
  * double-fire harmless, but the policy still returns a single action.
37
+ *
38
+ * R6 WAIT FOR THE FREE REFILL (owner, 2026-10-23) — a wall redeem (R1) burns a card
39
+ * that z.ai's free weekly refill is about to make redundant: when the refill
40
+ * lands <24h away and weekly util < 0.98, HOLD the card. At >= 0.98 redeem
41
+ * immediately — the owner is never blocked waiting for a free refill that is
42
+ * merely hours away. Dying-value redeems (R2) are exempt: an expiring card's
43
+ * value cannot survive a wait.
37
44
  */
38
45
  export const POLICY = {
39
46
  wallWeekly: 0.90, // weekly at 90% = at the wall
@@ -41,6 +48,8 @@ export const POLICY = {
41
48
  dyingWindowMs: 24 * 3600 * 1000, // redeem-don't-save horizon
42
49
  dyingMinUtilWeekly: 0.30, // below this a dying weekly refill buys too little
43
50
  dyingMinUtilSes: 0.50, // 5h refills are cheap capacity; demand more waste first
51
+ freeRefillWaitMs: 24 * 3600 * 1000, // R6: hold a wall redeem this close to the free refill
52
+ freeRefillImmediate: 0.98, // R6: ...unless this full — never block the owner
44
53
  };
45
54
 
46
55
  function msLeft(t, now) { return t == null ? Infinity : t - now; }
@@ -70,6 +79,13 @@ export function decideZai(account, cards, now = Date.now()) {
70
79
  const dying = msLeft(weeklyCard.expiresAt, now) < POLICY.dyingWindowMs
71
80
  && worst >= POLICY.dyingMinUtilWeekly;
72
81
  if (atWall || dying) {
82
+ // R6: a wall redeem waits when the FREE weekly refill is imminent and the
83
+ // account isn't critically full (dying-value redeems stay exempt above).
84
+ if (atWall && !dying && q.providerWkReset != null
85
+ && msLeft(q.providerWkReset, now) < POLICY.freeRefillWaitMs
86
+ && wkUtil < POLICY.freeRefillImmediate) {
87
+ return null; // hold — the free refill lands before the card would matter
88
+ }
73
89
  return { kind: 'card', card: weeklyCard, resetType: 'WEEK',
74
90
  reason: atWall ? 'weekly at wall' : 'dying value' };
75
91
  }
@@ -85,15 +85,19 @@ export async function listResetCards(token, { signal, fetchImpl = fetch } = {})
85
85
  }
86
86
 
87
87
  /** Deterministic requestId for a card: retries of the SAME redeem are idempotent
88
- * server-side, so a network blip mid-redeem can never double-spend. */
89
- function requestIdFor(recordId) {
90
- const h = createHash('sha256').update(`maxpool-zai-card-${recordId}`).digest('hex').slice(0, 24);
88
+ * server-side, so a network blip mid-redeem can never double-spend. The account
89
+ * key scopes it too — recordIds collide across z.ai accounts (card 9 on max+1 AND
90
+ * glm4, 2026-10-23); z.ai idempotency measured per-account, but scoping keeps the
91
+ * two accounts' keys distinct even if that assumption ever changes (red-team).
92
+ * accountKey never changes semantics for a given account, so retries stay stable. */
93
+ function requestIdFor(recordId, accountKey = '') {
94
+ const h = createHash('sha256').update(`maxpool-zai-card-${accountKey}-${recordId}`).digest('hex').slice(0, 24);
91
95
  return `maxpool-${h}`;
92
96
  }
93
97
 
94
98
  /** Redeem one card. NEVER retries on the wire here (the idempotency key makes a
95
99
  * caller-level retry safe). Returns { ok:true, recordId } | { ok:false, error, code }. */
96
- export async function redeemResetCard(token, recordId, resetType, { grantType, signal, fetchImpl = fetch } = {}) {
100
+ export async function redeemResetCard(token, recordId, resetType, { grantType, accountKey = '', signal, fetchImpl = fetch } = {}) {
97
101
  if (resetType !== 'WEEK' && resetType !== 'FIVE_HOUR') {
98
102
  return { ok: false, error: `invalid resetType ${resetType}` };
99
103
  }
@@ -105,7 +109,7 @@ export async function redeemResetCard(token, recordId, resetType, { grantType, s
105
109
  // grantType answers "Unsupported coupon grant type" without spending anything.
106
110
  const body = {
107
111
  recordId, targetType: 'PERSONAL', resetType,
108
- requestId: requestIdFor(recordId),
112
+ requestId: requestIdFor(recordId, accountKey),
109
113
  };
110
114
  if (grantType) body.grantType = grantType;
111
115
  const res = await fetchImpl(USE_URL, {