openzoo 0.51.24 → 0.51.26

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/lib/proxy.js +31 -0
  2. package/package.json +1 -1
package/lib/proxy.js CHANGED
@@ -998,6 +998,37 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
998
998
  result = await client.fetch(url, init);
999
999
  }
1000
1000
  }
1001
+ // A 5xx IS NOT A REASON TO CHANGE THE MODEL. An earlier build routed any
1002
+ // gateway 5xx to OPENZOO_OUTAGE_FALLBACK and gated the model 60s. That
1003
+ // was wrong twice over: `/model fable-5.1` then reported `x402 door for
1004
+ // grok-4.6 failed`, so the caller read an error for a model they never
1005
+ // picked, and a silent substitution bills them for a DIFFERENT model's
1006
+ // answer than the one they chose and are reading. When the requested
1007
+ // model's door is down, say so about THAT model and let the caller
1008
+ // decide. Only the 402 "insufficient credits" branch above still
1009
+ // reroutes, because there the gateway itself refunds and the request
1010
+ // was never served at all.
1011
+ const RETRYABLE_5XX = new Set([500, 502, 503, 504]);
1012
+ if (RETRYABLE_5XX.has(result.response?.status)) {
1013
+ let detail = '';
1014
+ try { const j = await result.response.clone().json(); detail = String(j?.error?.message || j?.error || '').slice(0, 300); } catch { /* opaque 5xx */ }
1015
+ log(`upstream ${result.response.status} for ${outageKey(init)}${detail ? `: ${detail}` : ''} — surfacing as-is, model NOT swapped`);
1016
+ // "no door quoted <model>: … wrong asset/rail (base)" IS AN EMPTY
1017
+ // WALLET WEARING A 502. Measured 2026-09-13: Solana USDC ran down to
1018
+ // dust, the rail picker fell through to Base (which held $0.0016), and
1019
+ // every door that only quotes Solana refused the offer — grok-4.6,
1020
+ // deepseek and fable all "502"d while abliterated-model (~$0.0005/call)
1021
+ // still paid out of the dust and looked fine. The raw 502 sent the
1022
+ // harness into a reconnect storm and read like a dead model. Say what
1023
+ // it is, and name the model the caller actually asked for.
1024
+ if (/no door quoted|wrong asset\/rail/i.test(detail)) {
1025
+ const rail = /\(([^)]+)\)/.exec(detail.match(/wrong asset\/rail \([^)]*\)/i)?.[0] || '')?.[1] || '';
1026
+ let msg = `${outageKey(init)} could not be paid for: the x402 door refused the offer${rail ? ` on the ${rail} rail` : ''}. This is almost always an empty wallet on the rail that door takes — the request never reached the model, and nothing was charged.`;
1027
+ msg = await withOnrampLink(msg, { solana: client.address, evm: client.evmAddress, force: true });
1028
+ jsonErr(res, 402, msg);
1029
+ return;
1030
+ }
1031
+ }
1001
1032
  // A PAID 400 `invalid_tools` on a Responses body we did not flatten:
1002
1033
  // this model is a function-tools-only door we had not learned yet.
1003
1034
  // Flatten, remember it, and buy the turn once more — one billed miss
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.51.24",
3
+ "version": "0.51.26",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",