maxpool 1.8.2 → 1.8.4

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.8.2",
3
+ "version": "1.8.4",
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",
@@ -15,11 +15,18 @@
15
15
  * Pre-mortem (2026-08-22) blockers B1/B2 + majors M3-M7 are the load-bearing comments.
16
16
  */
17
17
 
18
+ // v3 (2026-08-23): every v2 row was written before the joined-mid-window fix, so each
19
+ // account's first cycle is a TAIL-only observation recorded as `complete` (measured on
20
+ // the live ledger: 14.6, 64.3, 64.4 and 74.4 minutes for 5h windows — all four rows it
21
+ // held). Rather than drop history a second time, v3 MIGRATES: closed v2 rows are kept
22
+ // and shown, marked partial with a reason, so they never reach an average and never
23
+ // re-alert. Forward rows are correct by construction.
24
+ //
18
25
  // v2 (2026-08-23): v1 histories are POISONED and are deliberately dropped on load.
19
26
  // The OAuth reset-stamp jitter (see AccountManager.noteCapacityWindowAdvance) shredded
20
27
  // real windows into slivers and recorded future-dated weekly cycles, so a v1 payload's
21
28
  // averages are wrong for weeks. Starting empty costs one window; keeping it costs trust.
22
- const SCHEMA_VERSION = 2;
29
+ const SCHEMA_VERSION = 3;
23
30
 
24
31
  // Two closes this far apart are the SAME boundary observed twice (a clock-close and a
25
32
  // stamp-advance racing across the boundary second), not two windows.
@@ -32,13 +39,39 @@ export class CapacityLedger {
32
39
  this._now = now;
33
40
  // accountName → { ses: {open?, closed: []}, wk: {open?, closed: []}, days: {utcDay: {tokens, partial}} }
34
41
  this._accounts = new Map();
42
+ // True only for a ledger restored with no usable history — see fromSerialized.
43
+ // Cleared per account+window once that window's first boundary is behind us.
44
+ this._joinedMidWindow = false;
35
45
  }
36
46
 
37
47
  /** Restore from a serialized payload (state.json). Tolerant: unknown schemaVersion →
38
48
  * start empty and KEEP the file (the caller preserves it); a corrupt shape → empty. */
39
49
  static fromSerialized(payload, { now = () => Date.now() } = {}) {
40
50
  const l = new CapacityLedger({ now });
41
- if (!payload || payload.schemaVersion !== SCHEMA_VERSION) return l;
51
+ // v2 → v3: keep the rows, demote them. They were all written before the
52
+ // joined-mid-window fix, so each is a partial observation mislabelled complete.
53
+ if (payload?.schemaVersion === 2) {
54
+ const migrated = { schemaVersion: SCHEMA_VERSION, accounts: {} };
55
+ for (const [name, rec] of Object.entries(payload.accounts || {})) {
56
+ migrated.accounts[name] = {
57
+ ses: { open: rec.ses?.open || null,
58
+ closed: (rec.ses?.closed || []).map(c => ({ ...c, complete: false, partialReason: 'pre-v3-unverified' })) },
59
+ wk: { open: rec.wk?.open || null,
60
+ closed: (rec.wk?.closed || []).map(c => ({ ...c, complete: false, partialReason: 'pre-v3-unverified' })) },
61
+ days: rec.days || {},
62
+ };
63
+ }
64
+ return CapacityLedger.fromSerialized(migrated, { now });
65
+ }
66
+ if (!payload || payload.schemaVersion !== SCHEMA_VERSION) {
67
+ // No history to continue: whatever window each account is in RIGHT NOW is
68
+ // already in progress, so the next cycle we close saw only its tail. Flag it so
69
+ // it is shown but never averaged (live 2026-08-23: the first GLM cycle after the
70
+ // v1.8.1 migration read 21,192 tokens for a 5h window it had watched 14.6 min —
71
+ // and with one cycle recorded, that sliver WAS every column).
72
+ l._joinedMidWindow = true;
73
+ return l;
74
+ }
42
75
  try {
43
76
  for (const [name, rec] of Object.entries(payload.accounts || {})) {
44
77
  const a = { ses: { open: null, closed: [] }, wk: { open: null, closed: [] }, days: {} };
@@ -91,7 +124,12 @@ export class CapacityLedger {
91
124
  // reset stamp was never learned). startedAt is the accrual time then — the cycle
92
125
  // will be flagged partial by the caller if the boot gap warrants it (B1/B2).
93
126
  if (!rec[w].open) {
94
- rec[w].open = { startedAt: at, tokensSoFar: 0, lastAccrualAt: at, complete: true, disabledDuring: false };
127
+ const joined = this._joinedMidWindow && rec[w].closed.length === 0;
128
+ rec[w].open = {
129
+ startedAt: at, tokensSoFar: 0, lastAccrualAt: at,
130
+ complete: !joined, disabledDuring: false,
131
+ ...(joined ? { partialReason: 'joined-mid-window' } : {}),
132
+ };
95
133
  }
96
134
  rec[w].open.tokensSoFar += input + output;
97
135
  rec[w].open.lastAccrualAt = at;
@@ -162,6 +200,7 @@ export class CapacityLedger {
162
200
  tokens: open.tokensSoFar,
163
201
  complete: open.complete,
164
202
  disabledDuring: open.disabledDuring,
203
+ ...(open.partialReason ? { partialReason: open.partialReason } : {}),
165
204
  resetAt,
166
205
  });
167
206
  if (rec[window].closed.length > MAX_CYCLES_PER_WINDOW) rec[window].closed.shift();