openzoo 0.48.15 → 0.48.17
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 +46 -9
- package/package.json +1 -1
package/lib/grokui.mjs
CHANGED
|
@@ -359,7 +359,16 @@ function newGroupThread(names) {
|
|
|
359
359
|
// sidecar appends to the same bound context instead of starting fresh.
|
|
360
360
|
const BIND_CHUNK_BYTES = 512 * 1024;
|
|
361
361
|
// Chained auto-run commands per user message. Each hop is a paid call.
|
|
362
|
-
|
|
362
|
+
// 8 was far too tight. A real task — install a toolchain, read a tree, wire a
|
|
363
|
+
// service — is dozens of directives, so auto kept halting mid-job and telling
|
|
364
|
+
// the user to type "continue", which is the exact thing auto exists to avoid.
|
|
365
|
+
// Still bounded, because every hop is a paid call, but bounded at a number
|
|
366
|
+
// that lets a job finish.
|
|
367
|
+
const AUTO_MAX_STEPS = Number(process.env.OZ_AUTO_MAX_STEPS || 60);
|
|
368
|
+
// An empty completion is a provider hiccup, not an answer. In auto we retry it
|
|
369
|
+
// silently rather than parking the thread behind a "say continue" note the
|
|
370
|
+
// user then has to answer — that turned a transient blip into a manual step.
|
|
371
|
+
const AUTO_EMPTY_RETRIES = Number(process.env.OZ_AUTO_EMPTY_RETRIES || 3);
|
|
363
372
|
// Ceiling on subagents per thread. Spawning is fire-and-forget and each child
|
|
364
373
|
// can spawn too, so without a count it is unbounded — MEASURED as 15+ threads
|
|
365
374
|
// all named tetris-contract, every one of them a live agent making paid calls.
|
|
@@ -516,7 +525,7 @@ function execCommand(command, cwd) {
|
|
|
516
525
|
exec(command, { cwd, shell: RUN_SHELL, timeout: RUN_TIMEOUT_MS, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
517
526
|
let out = (stdout || '') + (stderr ? '\n' + stderr : '');
|
|
518
527
|
if (err) out += `\n(exit ${err.code ?? 1})`;
|
|
519
|
-
resolve(out
|
|
528
|
+
resolve(keepWhole(out) || '(no output)');
|
|
520
529
|
});
|
|
521
530
|
});
|
|
522
531
|
}
|
|
@@ -572,6 +581,26 @@ const usd = (n) => (n >= 0.01 || n === 0 ? '$' + n.toFixed(2) : '$' + n.toFixed(
|
|
|
572
581
|
//
|
|
573
582
|
// So: feed back the head and tail (where the answer nearly always is) and say
|
|
574
583
|
// plainly that the middle is recallable rather than lost.
|
|
584
|
+
// Directive output goes into t.history VERBATIM, because bindThread() binds
|
|
585
|
+
// history — so whatever is cut here is not "elided", it is DESTROYED before
|
|
586
|
+
// the holographic context ever sees it.
|
|
587
|
+
//
|
|
588
|
+
// These used to be hard slices at 4-8k right where the output was produced:
|
|
589
|
+
// a command's stdout, a READ, a FETCH, an MCP tool result. The user saw
|
|
590
|
+
// "…(truncated)" and the rest was simply gone — unrecallable, because it was
|
|
591
|
+
// never stored. That is the opposite of what this product does.
|
|
592
|
+
//
|
|
593
|
+
// So: keep the whole thing (up to a sanity ceiling that exists only to stop a
|
|
594
|
+
// runaway `yes` or a binary dump from bloating the thread store), let it bind,
|
|
595
|
+
// and let condense() decide how much the MODEL sees on the next hop. Big
|
|
596
|
+
// outputs are exactly the case leCore is for.
|
|
597
|
+
const KEEP_MAX = Number(process.env.OZ_KEEP_MAX || 400000);
|
|
598
|
+
function keepWhole(text) {
|
|
599
|
+
const s = String(text ?? '');
|
|
600
|
+
if (s.length <= KEEP_MAX) return s;
|
|
601
|
+
return `${s.slice(0, KEEP_MAX)}\n…[${s.length - KEEP_MAX} chars over the ${KEEP_MAX}-char store ceiling and dropped]`;
|
|
602
|
+
}
|
|
603
|
+
|
|
575
604
|
const FEEDBACK_MAX = Number(process.env.OZ_FEEDBACK_MAX || 3000);
|
|
576
605
|
function condense(label, text) {
|
|
577
606
|
const s = String(text ?? '');
|
|
@@ -908,7 +937,7 @@ async function tryDirective(reply, originId) {
|
|
|
908
937
|
const rel = readD[1].trim();
|
|
909
938
|
try {
|
|
910
939
|
const data = readFileSync(safeResolveIn(dirFor(originId), rel), 'utf8');
|
|
911
|
-
return `${rel}:\n${
|
|
940
|
+
return `${rel}:\n${keepWhole(data)}`;
|
|
912
941
|
} catch (e) { return `Couldn't read ${rel}: ${e.message}`; }
|
|
913
942
|
}
|
|
914
943
|
// EDIT beats WRITE for changing part of a file: WRITE overwrites the whole
|
|
@@ -1090,7 +1119,7 @@ async function tryDirective(reply, originId) {
|
|
|
1090
1119
|
+ `directive instead:\n MCP: ${url}\nto list its tools, then\n `
|
|
1091
1120
|
+ `MCP: ${url} | <tool> | {"arg": "value"}\nto call one.`;
|
|
1092
1121
|
}
|
|
1093
|
-
return `${url} (${r.status}):\n${
|
|
1122
|
+
return `${url} (${r.status}):\n${keepWhole(text)}`;
|
|
1094
1123
|
} catch (e) { return `Couldn't fetch ${url}: ${e.message}`; }
|
|
1095
1124
|
}
|
|
1096
1125
|
|
|
@@ -1194,7 +1223,7 @@ async function mcpDirective(url, tool, args) {
|
|
|
1194
1223
|
const out = (res?.content || [])
|
|
1195
1224
|
.map((c) => (c.type === 'text' ? c.text : `[${c.type}]`))
|
|
1196
1225
|
.join('\n') || JSON.stringify(res ?? call.raw);
|
|
1197
|
-
return `${banner}\n$ ${tool}\n${
|
|
1226
|
+
return `${banner}\n$ ${tool}\n${keepWhole(out)}`;
|
|
1198
1227
|
} catch (e) {
|
|
1199
1228
|
return `MCP ${url}: ${e.message}`;
|
|
1200
1229
|
}
|
|
@@ -1288,8 +1317,16 @@ async function runTurn(threadId, userText, onEvent, images) {
|
|
|
1288
1317
|
// user a turn and told them nothing. Retry once before giving up, and if
|
|
1289
1318
|
// it is still empty, say what actually happened instead of "(no
|
|
1290
1319
|
// response)", which reads like the harness broke.
|
|
1291
|
-
|
|
1292
|
-
|
|
1320
|
+
// Retry in place rather than parking the thread behind a note the user has
|
|
1321
|
+
// to answer. In auto especially: a transient blip must not become a manual
|
|
1322
|
+
// step, which is the whole point of auto.
|
|
1323
|
+
for (let i = 0; !reply && i < AUTO_EMPTY_RETRIES; i++) {
|
|
1324
|
+
await new Promise((r) => setTimeout(r, 400 * (i + 1)));
|
|
1325
|
+
reply = await ask();
|
|
1326
|
+
}
|
|
1327
|
+
if (!reply) {
|
|
1328
|
+
reply = `(the model returned nothing ${AUTO_EMPTY_RETRIES + 1} times — that is a provider problem, not your input. Try /model to switch, or send anything to retry.)`;
|
|
1329
|
+
}
|
|
1293
1330
|
} catch (e) {
|
|
1294
1331
|
reply = `error: ${e.message}`;
|
|
1295
1332
|
}
|
|
@@ -1323,7 +1360,7 @@ async function runTurn(threadId, userText, onEvent, images) {
|
|
|
1323
1360
|
bindThread(t).catch(() => {});
|
|
1324
1361
|
runTurn(threadId, condense('(command output)', output), onEvent).catch(() => {});
|
|
1325
1362
|
} else {
|
|
1326
|
-
const note = `(auto-run
|
|
1363
|
+
const note = `(auto-run paused after ${AUTO_MAX_STEPS} chained commands — that is the spend ceiling, not the end of the job. Send anything to carry on, or raise OZ_AUTO_MAX_STEPS.)`;
|
|
1327
1364
|
t.history.push({ who: 'bot', text: note });
|
|
1328
1365
|
onEvent?.({ type: 'final', name: t.name, color: t.color, text: note });
|
|
1329
1366
|
saveThreads();
|
|
@@ -1368,7 +1405,7 @@ async function runTurn(threadId, userText, onEvent, images) {
|
|
|
1368
1405
|
runTurn(threadId, condense('(directive result)', ack), onEvent).catch(() => {});
|
|
1369
1406
|
return;
|
|
1370
1407
|
}
|
|
1371
|
-
const note = `(auto
|
|
1408
|
+
const note = `(auto paused after ${AUTO_MAX_STEPS} steps — that is the spend ceiling, not the end of the job. Send anything to carry on, or raise OZ_AUTO_MAX_STEPS.)`;
|
|
1372
1409
|
t.history.push({ who: 'bot', text: note });
|
|
1373
1410
|
onEvent?.({ type: 'final', name: t.name, color: t.color, text: note });
|
|
1374
1411
|
saveThreads();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.17",
|
|
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",
|