openzoo 0.50.59 → 0.50.60

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.
@@ -2650,7 +2650,7 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
2650
2650
  return [
2651
2651
  `Host MCP tools from the user's local Claude/Grok config are attached (${servers.join(', ') || 'mcp'}): ${names.slice(0, 36).join(', ')}${names.length > 36 ? '…' : ''}.`,
2652
2652
  chrome.length
2653
- ? `For any web page or HTML form, use chrome-devtools tools (navigate_page / take_snapshot / fill / click). Do NOT use osascript, Quartz, Python Foundation, or AppleScript to read Brave. screenshot/click/type_text are for native Mac UI only. Browser mode: ${chromeStatus().mode}.${chromeStatus().hint ? ` This Chrome is NOT the human's browser: a separate profile with no logins. If the human asks for their logged-in Chrome / Google / X / "the same chrome", do NOT open pages in this one and do NOT spawn anything — reply with exactly this and stop: ${chromeStatus().hint}` : ' This is the human\'s real logged-in browser; do not log out or change its settings.'}`
2653
+ ? `For any web page or HTML form, use chrome-devtools tools (navigate_page / take_snapshot / fill / click). Do NOT use osascript, Quartz, Python Foundation, or AppleScript to read Brave. screenshot/click/type_text are for native Mac UI only. On heavy pages (x.com, gmail, big SPAs) take_snapshot can hang: prefer evaluate_script that returns only the text you need, or take_screenshot; a tool that times out says so — do not repeat it. Do not re-run schedule_wakeup or list_agents every turn; once is enough. Browser mode: ${chromeStatus().mode}.${chromeStatus().hint ? ` This Chrome is NOT the human's browser: a separate profile with no logins. If the human asks for their logged-in Chrome / Google / X / "the same chrome", do NOT open pages in this one and do NOT spawn anything — reply with exactly this and stop: ${chromeStatus().hint}` : ' This is the human\'s real logged-in browser; do not log out or change its settings.'}`
2654
2654
  : 'Use matching MCP tools instead of inventing shell one-liners.',
2655
2655
  ].join(' ');
2656
2656
  })(),
package/lib/mcpbridge.js CHANGED
@@ -325,7 +325,7 @@ async function connectOne(cfg, log) {
325
325
  try {
326
326
  transport.stderr?.on?.('data', (buf) => {
327
327
  const line = String(buf).trim().split('\n')[0];
328
- if (line) log?.(`cursor-backend: mcp ${cfg.name} ${line.slice(0, 160)}`);
328
+ if (line && !/No handler registered for issue code/.test(line)) log?.(`cursor-backend: mcp ${cfg.name} ${line.slice(0, 160)}`);
329
329
  });
330
330
  } catch { /* */ }
331
331
  }
@@ -389,11 +389,33 @@ function rebuildOpenai() {
389
389
  }
390
390
  }
391
391
 
392
- export async function callHostMcp(name, args) {
392
+ /**
393
+ * One MCP call, with a hard ceiling. Measured 2026-09-01: take_snapshot on
394
+ * x.com never returned and the whole zoo turn froze behind it. A tool that
395
+ * hangs must come back as an error string the model can route around.
396
+ */
397
+ export const MCP_CALL_TIMEOUT_MS = Math.max(10_000, Number(process.env.OZ_MCP_CALL_TIMEOUT_MS || 75_000));
398
+ export async function callHostMcp(name, args, { timeoutMs = MCP_CALL_TIMEOUT_MS } = {}) {
393
399
  const rec = registry.get(String(name || ''));
394
400
  if (!rec) throw new Error(`no mcp tool ${name}`);
395
- const r = await rec.client.callTool({ name: rec.tool, arguments: args && typeof args === 'object' ? args : {} });
396
- return flattenMcpResult(r);
401
+ let timer;
402
+ const ceiling = new Promise((_, rej) => {
403
+ timer = setTimeout(() => rej(new Error(`MCP tool ${name} timed out after ${Math.round(timeoutMs / 1000)}s. The page may be too heavy for that action. Use a narrower one: evaluate_script to pull the text you need, take_screenshot, or navigate_page to a lighter URL. Do not retry the same call.`)), timeoutMs);
404
+ timer.unref?.();
405
+ });
406
+ try {
407
+ const r = await Promise.race([
408
+ rec.client.callTool({ name: rec.tool, arguments: args && typeof args === 'object' ? args : {} }),
409
+ ceiling,
410
+ ]);
411
+ return flattenMcpResult(r);
412
+ } finally {
413
+ clearTimeout(timer);
414
+ }
415
+ }
416
+
417
+ export function registerHostMcpForTests(name, rec) {
418
+ registry.set(name, rec);
397
419
  }
398
420
 
399
421
  export function resetHostMcpForTests() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.50.59",
3
+ "version": "0.50.60",
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",