openzoo 0.50.45 → 0.50.47
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/cursorbackend.js +70 -9
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -1264,15 +1264,63 @@ function mintLocalAgent(parsed = {}) {
|
|
|
1264
1264
|
seedBriefOnCanvas(agent);
|
|
1265
1265
|
return agent;
|
|
1266
1266
|
}
|
|
1267
|
-
|
|
1267
|
+
|
|
1268
|
+
function entryKickText(e) {
|
|
1269
|
+
if (!e || typeof e !== 'object') return '';
|
|
1270
|
+
if (e.kind === 'send-message') return String(e.message?.content || '');
|
|
1271
|
+
return String(e.content || '');
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
const briefKicked = new Set();
|
|
1275
|
+
function needsJobKick(agent) {
|
|
1268
1276
|
if (!agent?.id || !String(agent.brief || '').trim()) return false;
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1277
|
+
if (briefKicked.has(agent.id)) return false;
|
|
1278
|
+
const entries = agentTranscript(agent.id).entries || [];
|
|
1279
|
+
if (!entries.length) return true;
|
|
1280
|
+
if (entries.length > 4) return false;
|
|
1281
|
+
const blob = entries.map(entryKickText).join('\n');
|
|
1282
|
+
if (/\[brief\]/i.test(blob) && entries.length <= 3) {
|
|
1283
|
+
if (/Working from this brief/i.test(blob)) return true;
|
|
1284
|
+
if (entries.length === 1) return true;
|
|
1285
|
+
}
|
|
1286
|
+
return false;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
function kickBriefedAgent(agent, log = () => {}) {
|
|
1290
|
+
if (!needsJobKick(agent)) return false;
|
|
1291
|
+
briefKicked.add(agent.id);
|
|
1292
|
+
const nonce = `oz-kick-${agent.id}`;
|
|
1293
|
+
log(`cursor-backend: brief-kick start ${agent.id} ${JSON.stringify(agent.name)}`);
|
|
1294
|
+
setImmediate(async () => {
|
|
1295
|
+
try {
|
|
1296
|
+
const z = await zooComplete(
|
|
1297
|
+
'Your brief is in this thread. Start that job now. Use tools. Do real work. Do not wait for another human message. Do not only say understood.',
|
|
1298
|
+
log,
|
|
1299
|
+
agent.id,
|
|
1300
|
+
{},
|
|
1301
|
+
);
|
|
1302
|
+
const line = fanoutLine(agent.id, 'assistant', z.text, { clientNonce: nonce, requestId: nonce });
|
|
1303
|
+
ssePush('transcript', { ...gatewayEntry(line), agentId: agent.id });
|
|
1304
|
+
bumpAgent(agent.id, { preview: z.text, notify: true });
|
|
1305
|
+
log(`cursor-backend: brief-kick done ${agent.id} seq=${line.seq}`);
|
|
1306
|
+
} catch (e) {
|
|
1307
|
+
briefKicked.delete(agent.id);
|
|
1308
|
+
log(`cursor-backend: brief-kick failed ${agent.id}: ${e.message}`);
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1274
1311
|
return true;
|
|
1275
1312
|
}
|
|
1313
|
+
|
|
1314
|
+
function seedBriefOnCanvas(agent, log) {
|
|
1315
|
+
if (!agent?.id || !String(agent.brief || '').trim()) return false;
|
|
1316
|
+
const t = agentTranscript(agent.id);
|
|
1317
|
+
if (!(t.entries || []).length) {
|
|
1318
|
+
const nonce = `oz-brief-${agent.id}`;
|
|
1319
|
+
const userLine = fanoutLine(agent.id, 'user', `[brief]\n${agent.brief}`, { clientNonce: nonce, requestId: nonce });
|
|
1320
|
+
ssePush('transcript', { ...gatewayEntry(userLine), agentId: agent.id });
|
|
1321
|
+
}
|
|
1322
|
+
return kickBriefedAgent(agent, log || ((m) => console.error(m)));
|
|
1323
|
+
}
|
|
1276
1324
|
function groupMemberIds(agentId) {
|
|
1277
1325
|
const a = (cachedAgentList() || []).find((x) => x.id === agentId);
|
|
1278
1326
|
if (!a?.isGroup) return [];
|
|
@@ -2235,6 +2283,7 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
2235
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.',
|
|
2236
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.',
|
|
2237
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.',
|
|
2238
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.',
|
|
2239
2288
|
].join(' '),
|
|
2240
2289
|
},
|
|
@@ -2295,9 +2344,11 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
2295
2344
|
}
|
|
2296
2345
|
if (!r) throw lastErr || new Error('zoo POST failed');
|
|
2297
2346
|
if (r.status === 402) {
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
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
|
+
}
|
|
2301
2352
|
}
|
|
2302
2353
|
const data = await r.json().catch(() => ({}));
|
|
2303
2354
|
if (r.status === 402) {
|
|
@@ -2395,6 +2446,16 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
2395
2446
|
log(`cursor-backend: << zoo ${r.status} ${text.length}c model=${data.model || model} finish=${finish}`);
|
|
2396
2447
|
// Model likes to park after research ("Stopped on research", "say go again")
|
|
2397
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
|
+
}
|
|
2398
2459
|
if (!chatOnly && (finish === 'length' || looksStoppedReply(text)) && keepGoingNudge < 6) {
|
|
2399
2460
|
keepGoingNudge += 1;
|
|
2400
2461
|
log(`cursor-backend: keep-going nudge=${keepGoingNudge} finish=${finish}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.47",
|
|
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",
|