openzoo 0.35.1 → 0.35.2

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 +24 -3
  2. package/package.json +1 -1
package/lib/proxy.js CHANGED
@@ -194,8 +194,29 @@ function serveAsSse(res, data, upstream) {
194
194
  */
195
195
  const REPLAY_TTL_MS = 30_000;
196
196
  const replayCache = new Map(); // sha256(body) -> { at, data, settle }
197
- function replayKey(bodyBuf) {
198
- return crypto.createHash('sha256').update(bodyBuf).digest('hex');
197
+ /**
198
+ * The key MUST include the routing headers, not just the body.
199
+ *
200
+ * Keying on the body alone was a correctness bug, not merely a caching one:
201
+ * N shards asking the SAME question of N DIFFERENT bound corpora send
202
+ * byte-identical bodies and differ only in X-HRR-Context. They collided on one
203
+ * key, so shards 2..N were served shard 1's answer — REPRODUCED in the field:
204
+ * 10 shards, 7 byte-identical replies across corpora known to differ, and the
205
+ * batch finished in 12s where a single uncached call took ~7s.
206
+ *
207
+ * Wrong answers attributed to the wrong corpus is a far worse failure than the
208
+ * double-billing this cache exists to prevent, so every header that can change
209
+ * the ANSWER joins the key.
210
+ */
211
+ const REPLAY_KEY_HEADERS = ['x-hrr-context', 'x-hrr-top-k', 'x-hrr-gate', 'x-openzoo-namespace'];
212
+
213
+ function replayKey(bodyBuf, headers = {}) {
214
+ const h = crypto.createHash('sha256').update(bodyBuf);
215
+ for (const name of REPLAY_KEY_HEADERS) {
216
+ const v = headers[name];
217
+ if (v) h.update(`\n${name}:${v}`);
218
+ }
219
+ return h.digest('hex');
199
220
  }
200
221
  function replayGet(key) {
201
222
  const hit = replayCache.get(key);
@@ -531,7 +552,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
531
552
  // Retry of a body we answered seconds ago? Serve the cached completion —
532
553
  // never pay twice for a harness's reconnect loop.
533
554
  const isChat = req.method === 'POST' && (req.url || '').includes('/chat/completions');
534
- const rKey = isChat ? replayKey(bodyBuf) : null;
555
+ const rKey = isChat ? replayKey(bodyBuf, req.headers) : null;
535
556
  if (rKey) {
536
557
  const hit = replayGet(rKey);
537
558
  if (hit) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.35.1",
3
+ "version": "0.35.2",
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",