maxpool 1.5.8 → 1.5.9
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 +1 -1
- package/src/account-manager.js +36 -9
package/package.json
CHANGED
package/src/account-manager.js
CHANGED
|
@@ -71,6 +71,14 @@ const DEFAULT_SCHEDULER = {
|
|
|
71
71
|
recoveryRampWeight: 4, // decaying penalty applied to a just-recovered account
|
|
72
72
|
recoveryRampMs: 5 * 60_000, // how long the post-recovery ramp lasts
|
|
73
73
|
spreadWindowMs: 15 * 60_000, // rolling window used to measure recent per-account load
|
|
74
|
+
// Allow a signed-thinking session to rebalance onto a DIFFERENT Claude account
|
|
75
|
+
// (never a provider — GLM/Kimi can't validate an Anthropic signature). Anthropic
|
|
76
|
+
// thinking-block signatures are content/model integrity, NOT account-bound —
|
|
77
|
+
// verified empirically 2026-07-02 (a `partnerships`-signed block replayed under
|
|
78
|
+
// `personal` returned 200). DEFAULT OFF until the revert-to-issuer fail-safe lands
|
|
79
|
+
// (issue #16): with it off, a heavy thinking session still can't strand an account,
|
|
80
|
+
// but it also can't poison a session if Anthropic ever changes the contract.
|
|
81
|
+
crossAccountThinkingMigration: false,
|
|
74
82
|
};
|
|
75
83
|
const LOAD_EVENT_MAX_AGE_MS = 60 * 60 * 1000;
|
|
76
84
|
const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -948,16 +956,31 @@ export class AccountManager {
|
|
|
948
956
|
* body signal only — never the session-sticky policy — and fails CLOSED on any
|
|
949
957
|
* body we couldn't fully scan (non-JSON / parse error → bodyThinkingScanned unset). */
|
|
950
958
|
_migrationSafeForRequest(requestInfo = {}) {
|
|
951
|
-
|
|
952
|
-
|
|
959
|
+
// Fail closed on any body we couldn't fully scan (non-JSON / parse error).
|
|
960
|
+
if (requestInfo.bodyThinkingScanned !== true) return false;
|
|
961
|
+
// A signed-thinking request is migration-safe when cross-account thinking
|
|
962
|
+
// migration is enabled: the signature is content/model integrity, not account-
|
|
963
|
+
// bound, and the rebalance candidate filter (_matchesRequest → _isRequestCompatible)
|
|
964
|
+
// already bars providers for thinking, so every eligible target is a Claude
|
|
965
|
+
// account. When the flag is off, keep the conservative bar (never migrate signed
|
|
966
|
+
// thinking) until the revert-to-issuer fail-safe lands (#16).
|
|
967
|
+
if (requestInfo.requiresAnthropicThinkingIntegrity === true) {
|
|
968
|
+
return this.scheduler.crossAccountThinkingMigration === true;
|
|
969
|
+
}
|
|
970
|
+
return true;
|
|
953
971
|
}
|
|
954
972
|
|
|
955
|
-
/** Flap-stable "hot":
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
973
|
+
/** Flap-stable "hot": burn-PACE reserve/critical/exhausted, or immediate session-
|
|
974
|
+
* quota pressure (5h cap or an API-key token/request limit via
|
|
975
|
+
* `_isSessionQuotaUnavailable`).
|
|
976
|
+
* Pace (not raw level) is the right trigger — it spreads a long/heavy session's
|
|
977
|
+
* later load onto fresh capacity BEFORE it exhausts one account, while a light or
|
|
978
|
+
* near-reset session (whose pace stays normal) never triggers → no churn. Does
|
|
979
|
+
* NOT flip the instant a request migrates (unlike live in-flight, left to the
|
|
980
|
+
* score loop), so a healthy bound account never ping-pongs. */
|
|
959
981
|
_isBoundAccountHot(account) {
|
|
960
|
-
return this.
|
|
982
|
+
return this._isSessionQuotaUnavailable(account)
|
|
983
|
+
|| ['reserve', 'critical', 'exhausted'].includes(this._weeklyPaceState(account));
|
|
961
984
|
}
|
|
962
985
|
|
|
963
986
|
/** Decide whether a bound session should leave its (hot) account THIS request.
|
|
@@ -970,7 +993,11 @@ export class AccountManager {
|
|
|
970
993
|
if (!this._isBoundAccountHot(bound)) return false;
|
|
971
994
|
|
|
972
995
|
const boundScore = this._scoreAccount(bound, requestInfo, scoringCtx);
|
|
973
|
-
|
|
996
|
+
// Tier guard on the SAME axis as the trigger (pace, not raw). If the trigger is
|
|
997
|
+
// pace but the tier guard is raw, a pace-hot fast-burner whose RAW tier is still
|
|
998
|
+
// `normal` can never find a strictly-healthier raw tier → migration never fires
|
|
999
|
+
// for the exact account this is meant to relieve. Match the axes.
|
|
1000
|
+
const boundTier = WEEKLY_TIER[this._weeklyPaceState(bound)] ?? 0;
|
|
974
1001
|
|
|
975
1002
|
let bestScore = Infinity;
|
|
976
1003
|
let bestTier = Infinity;
|
|
@@ -983,7 +1010,7 @@ export class AccountManager {
|
|
|
983
1010
|
const score = this._scoreAccount(account, requestInfo, scoringCtx);
|
|
984
1011
|
if (score < bestScore) {
|
|
985
1012
|
bestScore = score;
|
|
986
|
-
bestTier = WEEKLY_TIER[this.
|
|
1013
|
+
bestTier = WEEKLY_TIER[this._weeklyPaceState(account)] ?? 0;
|
|
987
1014
|
}
|
|
988
1015
|
}
|
|
989
1016
|
if (!Number.isFinite(bestScore)) return false; // no healthy alternative
|