maxpool 1.5.74 → 1.5.75

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/server.js +25 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maxpool",
3
- "version": "1.5.74",
3
+ "version": "1.5.75",
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/server.js CHANGED
@@ -1093,8 +1093,14 @@ async function forwardRequest(
1093
1093
  // its life. Verified against the real API — the stripped history returns 200, with
1094
1094
  // text + tool_use preserved. Tried once per request (thinkingStripped guard); if it
1095
1095
  // still fails, the provider pin below is the fallback.
1096
- if (isSignatureRejection && !requestInfo.thinkingStripped
1097
- && canRetryBufferedBody && canRepairBody) {
1096
+ // NOT gated on canRetryBufferedBody. That limit exists to stop a huge body being
1097
+ // re-sent across accounts on a FAILOVER — but a repair SHRINKS the body (measured:
1098
+ // 11.5MB → 5.7MB in 20ms) and is the only thing standing between the user and a
1099
+ // dead session. The body is already fully in memory by this point, so refusing to
1100
+ // rewrite it saves nothing; it just guarantees the 400 surfaces. Reported
1101
+ // 2026-08-10: "history too large to rewrite automatically" on a session the strip
1102
+ // would have fixed in 20ms. The retry it schedules re-checks the SHRUNK size.
1103
+ if (isSignatureRejection && !requestInfo.thinkingStripped && canRepairBody) {
1098
1104
  console.log(`[Maxpool] Anthropic rejected a block: ${describeRejectedBlock(body, errorBody)}`);
1099
1105
  const { body: cleanBody, removed, converted } = stripForeignThinkingBlocks(body);
1100
1106
  if (cleanBody) {
@@ -1102,10 +1108,14 @@ async function forwardRequest(
1102
1108
  // rejected round-trip (the client resends the full poisoned history each turn).
1103
1109
  accountManager.markSessionThinkingContaminated?.(requestInfo.sessionKey);
1104
1110
  console.log(`[Maxpool] Recovering session on Claude: stripped ${removed} provider thinking block(s), converted ${converted} provider search block(s) to text`);
1111
+ // Re-derive the retry budget from the SHRUNK body. Inheriting the old flag
1112
+ // would leave a now-5.7MB body permanently marked "too big to retry" because
1113
+ // it was 11.5MB before the repair — barring the very failover the repair
1114
+ // exists to enable.
1105
1115
  return forwardRequest(
1106
1116
  req, res, cleanBody, accountManager, upstream, retryCount + 1, hooks, reqId, ctx, logDir,
1107
1117
  retryConfig, queueConfig, { ...requestInfo, thinkingStripped: true, repairCount: repairCount + 1 },
1108
- canRetryBufferedBody, canQueueBufferedBody, excludedIndexes,
1118
+ cleanBody.length <= retryConfig.maxRetryBufferBytes, canQueueBufferedBody, excludedIndexes,
1109
1119
  );
1110
1120
  }
1111
1121
  }
@@ -1113,7 +1123,9 @@ async function forwardRequest(
1113
1123
  // COORDINATE REPAIR (runs after the broad strip found nothing, or found the wrong
1114
1124
  // thing). Anthropic pointed at an exact block index; trust that over our own shape
1115
1125
  // model. Its own flag, so it still fires on a request whose broad strip already ran.
1116
- if (isSignatureRejection && !requestInfo.rejectedBlockStripped && canRetryBufferedBody && canRepairBody) {
1126
+ // Same reasoning as the broad strip above: a repair SHRINKS the body, so the
1127
+ // re-send limit must not bar it.
1128
+ if (isSignatureRejection && !requestInfo.rejectedBlockStripped && canRepairBody) {
1117
1129
  const { body: fixedBody, removed, type } = stripRejectedBlockClass(body, errorBody);
1118
1130
  if (fixedBody) {
1119
1131
  // Latch the session ONLY for the class the pre-strip can actually repair up
@@ -1133,7 +1145,7 @@ async function forwardRequest(
1133
1145
  thinkingStripped: preStripCanRepeat || requestInfo.thinkingStripped,
1134
1146
  repairCount: repairCount + 1,
1135
1147
  },
1136
- canRetryBufferedBody, canQueueBufferedBody, excludedIndexes,
1148
+ fixedBody.length <= retryConfig.maxRetryBufferBytes, canQueueBufferedBody, excludedIndexes,
1137
1149
  );
1138
1150
  }
1139
1151
  }
@@ -1198,16 +1210,14 @@ async function forwardRequest(
1198
1210
  // and without it the two surviving explanations (a block the strip cannot see
1199
1211
  // vs. a body over the retry buffer) are indistinguishable in the log.
1200
1212
  console.log(`[Maxpool] Unrepaired signature 400: ${describeRejectedBlock(body, errorBody)} bufferable=${canRetryBufferedBody} stripped=${!!requestInfo.thinkingStripped}`);
1201
- // A body over the retry buffer bars EVERY repair above without a word — the user
1202
- // then sees "could not repair" for a transcript maxpool never even tried to fix.
1203
- // Name that separately so the reason is actionable rather than mysterious.
1204
- // `peek` (not the full class-strip) because on THIS path the body may be the very
1205
- // one just declared too large to rewrite — a full parse+rebuild there costs 15ms
1206
- // and a discarded 4.8MB Buffer on a 9.6MB body, to read one string.
1207
- const rejectedType = canRetryBufferedBody ? peekRejectedBlockType(body, errorBody) : null;
1208
- const what = !canRetryBufferedBody
1209
- ? `This session's history is too large for maxpool to rewrite automatically (over ${Math.round(retryConfig.maxRetryBufferBytes / (1024 * 1024))}MB). Run /compact and it will keep going.`
1210
- : rejectedType && rejectedType !== 'thinking' && rejectedType !== 'redacted_thinking'
1213
+ // `peek` (not the full class-strip) — reads only `.type`, no parse+rebuild.
1214
+ // Read it regardless of body size: the repairs are no longer size-gated, so a
1215
+ // large body reaches here for a REAL reason (an unremovable block type) and the
1216
+ // user deserves to be told which one.
1217
+ const rejectedType = peekRejectedBlockType(body, errorBody);
1218
+ // NOTE: no "too large to rewrite" branch any more — the repairs above are no
1219
+ // longer gated on canRetryBufferedBody, so size never blocks a repair attempt.
1220
+ const what = rejectedType && rejectedType !== 'thinking' && rejectedType !== 'redacted_thinking'
1211
1221
  ? `Anthropic rejected a "${rejectedType}" block in this session's history, which maxpool cannot remove without losing conversation content.`
1212
1222
  : requestInfo.thinkingStripped || requestInfo.rejectedBlockStripped
1213
1223
  ? 'This session ran on GLM/Kimi earlier, and Anthropic will not accept parts of what they wrote. Maxpool repaired what it could and retried on Claude, but Anthropic still rejected the history.'