openzoo 0.43.4 → 0.43.6

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/lib/grokui.mjs CHANGED
@@ -115,7 +115,13 @@ Via RUN you can also make YOUR OWN paid openzoo calls — POST to
115
115
  http://localhost:8402/v1/chat/completions (or /v1/hrr/bind) with curl/python/etc. Auth is
116
116
  "Authorization: Bearer sk-openzoo" — any string works, x402 pays per call, not the key. Do
117
117
  NOT tell the user you "can't fire the paid calls" or need "their client's bearer key" —
118
- that's wrong, you can make these calls yourself via RUN.
118
+ that's wrong, you can make these calls yourself via RUN. When you do, set max_tokens
119
+ generously (1000+, not 50) — a reasoning model can burn its ENTIRE budget on internal
120
+ reasoning before writing any visible answer, especially against a large bound corpus, and
121
+ comes back with content:null and finish_reason:"length" (confirmed live) if you starve it.
122
+ /v1/hrr/bind also caps around ~8MB per request after JSON-escaping — chunk large corpora
123
+ (e.g. 512KB raw per request) and pass the PREVIOUS chunk's context_id on each next request
124
+ to append to the same bound context, rather than one giant request that silently fails partway.
119
125
  For normal questions just answer directly — do not use any of these unless the request
120
126
  actually calls for delegation or file work.`;
121
127
 
@@ -230,17 +236,32 @@ function newGroupThread(names) {
230
236
  // turn's brain()/brainStream() call picks up t.contextId once it lands, via
231
237
  // the X-HRR-Context header, so retrieval is real and automatic, not a prompt
232
238
  // claim about a mechanism that doesn't exist.
239
+ // Chunked, not one shot: a single request over ~8MB (post JSON-escaping)
240
+ // gets rejected, so a large/growing thread's bind would silently fail past
241
+ // whatever point it crossed that line — confirmed live by a bot's own RUN
242
+ // diagnostic ("Failed at chunk 2 — JSON escaping pushed a 3MB chunk over the
243
+ // ~8MB request limit"). 512KB raw per request leaves wide margin. Each
244
+ // chunk after the first carries the PREVIOUS chunk's context_id so the
245
+ // sidecar appends to the same bound context instead of starting fresh.
246
+ const BIND_CHUNK_BYTES = 512 * 1024;
233
247
  async function bindThread(t) {
234
248
  const corpus = t.history.map((h) => (h.who === 'user' ? 'you' : (h.name || t.name)) + ': ' + h.text).join('\n');
235
249
  if (!corpus.trim()) return;
236
250
  try {
237
- const r = await fetch(`${PROXY}/hrr/bind`, {
238
- method: 'POST',
239
- headers: { 'content-type': 'application/json' },
240
- body: JSON.stringify({ corpus }),
241
- });
242
- const j = await r.json().catch(() => ({}));
243
- if (j?.context_id) { t.contextId = j.context_id; saveThreads(); }
251
+ let ctx;
252
+ for (let i = 0; i < corpus.length; i += BIND_CHUNK_BYTES) {
253
+ const part = corpus.slice(i, i + BIND_CHUNK_BYTES);
254
+ const body = ctx ? { corpus: part, context_id: ctx } : { corpus: part };
255
+ const r = await fetch(`${PROXY}/hrr/bind`, {
256
+ method: 'POST',
257
+ headers: { 'content-type': 'application/json' },
258
+ body: JSON.stringify(body),
259
+ });
260
+ const j = await r.json().catch(() => ({}));
261
+ if (j?.context_id) ctx = j.context_id;
262
+ else break; // this chunk failed — stop, keep whatever bound so far rather than lose it all
263
+ }
264
+ if (ctx) { t.contextId = ctx; saveThreads(); }
244
265
  } catch { /* leCore sidecar unreachable — thread still works, just not bound this round */ }
245
266
  }
246
267
 
@@ -639,6 +660,7 @@ const APP_HTML = `<!doctype html>
639
660
  <div class="hrow"><span>our cost (cogs)</span><span id="hYouCogs">—</span></div>
640
661
  <div class="hrow"><span>margin</span><span id="hYouMargin" class="hlime">—</span></div>
641
662
  <div class="hrow"><span>direct would be</span><span id="hYouDirect" class="hember">—</span></div>
663
+ <div class="hrow"><span>saved vs. naked calls</span><span id="hYouSaved" class="hlime">—</span></div>
642
664
  <div class="hfoot" id="hFoot">loading…</div>
643
665
  </div>
644
666
  <div id="log"></div>
@@ -1034,6 +1056,17 @@ const APP_HTML = `<!doctype html>
1034
1056
  document.getElementById('hYouCogs').textContent = usd(cogs);
1035
1057
  document.getElementById('hYouMargin').textContent = margin;
1036
1058
  document.getElementById('hYouDirect').textContent = usd(direct);
1059
+ const savedEl = document.getElementById('hYouSaved');
1060
+ if (spent > 0) {
1061
+ const mult = direct / spent;
1062
+ // honest either way: >=1x is a real saving vs a naked direct call,
1063
+ // <1x means you're currently paying MORE than direct would cost —
1064
+ // don't dress that up as green when it isn't one
1065
+ savedEl.textContent = mult.toFixed(2) + 'x';
1066
+ savedEl.className = mult >= 1 ? 'hlime' : 'hember';
1067
+ } else {
1068
+ savedEl.textContent = '—';
1069
+ }
1037
1070
  document.getElementById('hFoot').textContent = (you.paidCalls || 0) + ' paid calls this session';
1038
1071
  } catch (e) {
1039
1072
  document.getElementById('hFoot').textContent = 'error: ' + e.message;
package/lib/podagent.mjs CHANGED
@@ -192,7 +192,7 @@ function hasImages(messages) {
192
192
  // grokui.mjs then renders as a generic "(no response)" — indistinguishable
193
193
  // from a model that genuinely had nothing to say. Callers should know WHY.
194
194
  function httpErrorNote(status) {
195
- if (status === 402) return '(payment failed — the wallet\'s x402 retry gave up after HTTP 402. Try again; if it keeps happening, check the wallet balance/RPC.)';
195
+ if (status === 402) return '(payment failed — the wallet\'s x402 retry gave up after HTTP 402, likely out of funds. Open http://localhost:8402 for this wallet\'s funding addresses (Solana USDC/TOKEN, Base USDC, Robinhood Chain) and current balance, or run `npx openzoo` in a terminal to see the same info. Try again once it\'s funded.)';
196
196
  if (status === 429) return '(rate limited — HTTP 429, try again in a moment)';
197
197
  if (status >= 500) return `(upstream error — HTTP ${status}, try again)`;
198
198
  return status ? `(request failed — HTTP ${status})` : '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.43.4",
3
+ "version": "0.43.6",
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",