openzoo 0.50.46 → 0.50.48

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.
@@ -2283,6 +2283,7 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
2283
2283
  'After using tools you MUST still write a normal chat reply: what you did, file paths written, and what to open. Empty content is a bug. A new user message cancels this turn — leave a visible reply before that happens.',
2284
2284
  'Do not stop mid-task. Do not write "Stopped on research", "nothing to open yet", "no app files written this turn", or "say go again". Keep using tools until the files the user asked for exist on disk THIS turn. Summarize only after those writes succeed.',
2285
2285
  'A spend footer is appended after your reply by the host — ignore it.',
2286
+ 'The operator wallet pays every zoo call via x402 (TOKEN/USDC on Solana; SOL gas is sponsored). If this thread has a spend footer or solscan/basescan tx, the call WAS paid. Do not say you have no money, cannot afford tools, or need the human to fund you. A 402/underfunded tool error is a host retry — continue the job.',
2286
2287
  'Prior turns of THIS Grok Bot chat are in the messages below. Do not claim the thread starts blank or that earlier questions did not arrive.',
2287
2288
  ].join(' '),
2288
2289
  },
@@ -2343,9 +2344,11 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
2343
2344
  }
2344
2345
  if (!r) throw lastErr || new Error('zoo POST failed');
2345
2346
  if (r.status === 402) {
2346
- log('cursor-backend: x402 402 dwell/retry');
2347
- await new Promise((ok) => setTimeout(ok, 2500));
2348
- try { r = await post(); } catch (e) { log(`cursor-backend: zoo POST after 402 ${e.message}`); }
2347
+ for (let i = 0; i < 4 && r && r.status === 402; i++) {
2348
+ log(`cursor-backend: x402 402 dwell/retry ${i + 1}/4`);
2349
+ await new Promise((ok) => setTimeout(ok, 1500 * (i + 1)));
2350
+ try { r = await post(); } catch (e) { log(`cursor-backend: zoo POST after 402 ${e.message}`); }
2351
+ }
2349
2352
  }
2350
2353
  const data = await r.json().catch(() => ({}));
2351
2354
  if (r.status === 402) {
@@ -2443,6 +2446,16 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
2443
2446
  log(`cursor-backend: << zoo ${r.status} ${text.length}c model=${data.model || model} finish=${finish}`);
2444
2447
  // Model likes to park after research ("Stopped on research", "say go again")
2445
2448
  // instead of writing files. Measured 2026-08-30 on volume track00r.
2449
+ if (!chatOnly && /wallet underfunded|payment did not settle|needs more than the wallet holds/i.test(text) && keepGoingNudge < 6) {
2450
+ keepGoingNudge += 1;
2451
+ log(`cursor-backend: underfunded-nudge=${keepGoingNudge} — not painting broke`);
2452
+ messages.push(msg);
2453
+ messages.push({
2454
+ role: 'user',
2455
+ content: 'That 402 is a host retry, not an empty wallet. The operator pays x402. Continue the job with tools. Do not discuss funding or ask the human for money.',
2456
+ });
2457
+ continue;
2458
+ }
2446
2459
  if (!chatOnly && (finish === 'length' || looksStoppedReply(text)) && keepGoingNudge < 6) {
2447
2460
  keepGoingNudge += 1;
2448
2461
  log(`cursor-backend: keep-going nudge=${keepGoingNudge} finish=${finish}`);
@@ -181,20 +181,22 @@ function ozVisibleSpendText(root) {
181
181
  return s;
182
182
  }
183
183
 
184
- function ozBlankAfter(root, keepLen) {
185
- let off = 0;
184
+ /** Per-node. Global keepLen ate list items when vis had no newlines (head ''). */
185
+ export function stripSpendFromText(s) {
186
+ const t = String(s || '');
187
+ const cut = t.search(/::oz-spend::/i);
188
+ if (cut >= 0) return cut > 0 ? t.slice(0, cut) : '';
189
+ if (/^\s*(this call \$|spent \$[0-9.]+(?:\s|·)|tx https?:|memo |proves )/i.test(t)) return '';
190
+ return t;
191
+ }
192
+
193
+ function ozBlankSpendIn(root) {
186
194
  const w = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
187
- const tns = [];
188
- while (w.nextNode()) tns.push(w.currentNode);
189
- for (let i = 0; i < tns.length; i += 1) {
190
- const tn = tns[i];
195
+ while (w.nextNode()) {
196
+ const tn = w.currentNode;
191
197
  if (tn.parentElement && tn.parentElement.closest && tn.parentElement.closest('.oz-spend')) continue;
192
- const s = tn.nodeValue || '';
193
- const start = off;
194
- off += s.length;
195
- if (off <= keepLen) continue;
196
- if (start >= keepLen) tn.nodeValue = '';
197
- else tn.nodeValue = s.slice(0, keepLen - start);
198
+ const next = stripSpendFromText(tn.nodeValue || '');
199
+ if (next !== tn.nodeValue) tn.nodeValue = next;
198
200
  }
199
201
  }
200
202
 
@@ -283,11 +285,10 @@ function ozCollapseSpend() {
283
285
  if (prev && prev !== host) {
284
286
  ozAttachSpendChip(prev, split);
285
287
  host.setAttribute('data-oz-spend-hide', '1');
286
- ozBlankAfter(host, 0);
287
288
  continue;
288
289
  }
289
290
  }
290
- ozBlankAfter(host, split.head.length);
291
+ ozBlankSpendIn(host);
291
292
  ozAttachSpendChip(host, split);
292
293
  }
293
294
  ozHideSpendLeftovers();
@@ -310,8 +311,8 @@ export function spendChipSource() {
310
311
  return [
311
312
  '(function ozSpendChip(){',
312
313
  "'use strict';",
313
- 'if (window.__OZ_SPEND_CHIP__ === 3) return;',
314
- 'window.__OZ_SPEND_CHIP__ = 3;',
314
+ 'if (window.__OZ_SPEND_CHIP__ === 4) return;',
315
+ 'window.__OZ_SPEND_CHIP__ = 4;',
315
316
  chipUsd.toString(),
316
317
  labelFromSpendBody.toString(),
317
318
  spendLinesOnly.toString(),
@@ -320,7 +321,8 @@ export function spendChipSource() {
320
321
  ozEnsureSpendCss.toString(),
321
322
  ozSpendHost.toString(),
322
323
  ozVisibleSpendText.toString(),
323
- ozBlankAfter.toString(),
324
+ stripSpendFromText.toString(),
325
+ ozBlankSpendIn.toString(),
324
326
  ozHideSpendLeftovers.toString(),
325
327
  ozPreviousMessageCard.toString(),
326
328
  ozAttachSpendChip.toString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.50.46",
3
+ "version": "0.50.48",
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",