openzoo 0.48.80 → 0.48.82
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/proxy.js +24 -13
- package/lib/spill.js +137 -27
- package/package.json +1 -1
package/lib/proxy.js
CHANGED
|
@@ -14,7 +14,7 @@ import { evmTokenBalance } from './evm.js';
|
|
|
14
14
|
import { bindCorpus, contextCacheDisabled, BIND_MIN_CHARS } from './hrr.js';
|
|
15
15
|
import {
|
|
16
16
|
loadBoundChars, noteCorpusLedger, filesForCorpus, readFilesForCorpus, boundAbsFromKeys,
|
|
17
|
-
createSpillStats, corpusCharsForSend, applySpillCut, msgText,
|
|
17
|
+
createSpillStats, corpusCharsForSend, applySpillCut, msgText, hudDollarX,
|
|
18
18
|
} from './spill.js';
|
|
19
19
|
import { rewritablePath, augmentModelList, ALIAS_IDS, rewriteChatModel, zooModelIds, CLASSIFY_MAX_TOKENS } from './models.js';
|
|
20
20
|
import { forgetContext } from './contexts.js';
|
|
@@ -351,7 +351,7 @@ function replayPut(key, data, settle) {
|
|
|
351
351
|
* severed at a plain `user` message — everything before one is self-contained.
|
|
352
352
|
* A system message is never spilled: it is the operating contract, not history.
|
|
353
353
|
*/
|
|
354
|
-
async function spillTranscript(body, log, req, stats) {
|
|
354
|
+
async function spillTranscript(body, log, req, stats, extra = {}) {
|
|
355
355
|
const msgs = Array.isArray(body?.messages) ? body.messages : null;
|
|
356
356
|
if (!msgs?.length) return null;
|
|
357
357
|
|
|
@@ -364,7 +364,8 @@ async function spillTranscript(body, log, req, stats) {
|
|
|
364
364
|
//
|
|
365
365
|
// Snapshot bound paths BEFORE collect so this turn's first-read files stay
|
|
366
366
|
// verbatim in the tail (not yet in the corpus for recall). Previously
|
|
367
|
-
// bound files
|
|
367
|
+
// bound files before the last ask may stub; remaining post-ask Read/Edit/
|
|
368
|
+
// Write / Bash-file bodies stay real (drop older rounds instead).
|
|
368
369
|
const previouslyBoundAbs = boundAbsFromKeys(boundFiles);
|
|
369
370
|
const fileCollect = filesForCorpus(msgs, { boundFiles });
|
|
370
371
|
const sessionId = req?.headers?.['x-claude-code-session-id']
|
|
@@ -422,8 +423,9 @@ async function spillTranscript(body, log, req, stats) {
|
|
|
422
423
|
}
|
|
423
424
|
|
|
424
425
|
// LIVE SELF-TUNER. Env knobs seed the first cut; after cut+stub the proxy
|
|
425
|
-
//
|
|
426
|
-
//
|
|
426
|
+
// scores the HUD dollar multiple (spill direct/billed) and retunes
|
|
427
|
+
// keep/min-turns/budget (stubMore for SEARCH, not live file bodies)
|
|
428
|
+
// in process memory so this request recuts when the green x is under 10.
|
|
427
429
|
// No restart. OPENZOO_ADAPT=0 freezes the env defaults. The ask always
|
|
428
430
|
// stays; we never drop below 2 real user/assistant turns to delete it.
|
|
429
431
|
const knownLedger = (sessionKey && spillMemo.get(sessionKey))
|
|
@@ -437,6 +439,8 @@ async function spillTranscript(body, log, req, stats) {
|
|
|
437
439
|
boundAbs: previouslyBoundAbs,
|
|
438
440
|
log,
|
|
439
441
|
persist: true,
|
|
442
|
+
dollarX: extra.dollarX ?? hudDollarX(stats || {}),
|
|
443
|
+
lastSend: extra.lastSend,
|
|
440
444
|
});
|
|
441
445
|
if (adapted.cut <= adapted.firstSpillable) {
|
|
442
446
|
bindFilesInBackground('no severable cut, files only');
|
|
@@ -628,9 +632,8 @@ async function spillTranscript(body, log, req, stats) {
|
|
|
628
632
|
? `transcript prefix already bound (${bind.contextId}, ${keyKind}) — sending ${sent}/${msgs.length} turns`
|
|
629
633
|
: `transcript prefix bound (${mb(bind.bytes)}MB → ${bind.contextId}, ${keyKind}) — sending ${sent}/${msgs.length} turns`);
|
|
630
634
|
|
|
631
|
-
//
|
|
632
|
-
//
|
|
633
|
-
// already logged there.
|
|
635
|
+
// applySpillCut cut/stubbed the tail (post-ask file bodies stay real;
|
|
636
|
+
// search may stub). The adapt line is already logged there.
|
|
634
637
|
if (stubbed.dropped) {
|
|
635
638
|
log(`file-stub stubbed=${stubbed.stubbed} dropped=${stubbed.dropped}`);
|
|
636
639
|
}
|
|
@@ -674,7 +677,7 @@ async function spillTranscript(body, log, req, stats) {
|
|
|
674
677
|
};
|
|
675
678
|
}
|
|
676
679
|
|
|
677
|
-
async function maybeCacheCorpus(req, bodyBuf, log, stats) {
|
|
680
|
+
async function maybeCacheCorpus(req, bodyBuf, log, stats, extra = {}) {
|
|
678
681
|
if (contextCacheDisabled()) return null;
|
|
679
682
|
if (req.method !== 'POST' || !(req.url || '').includes('/chat/completions')) return null;
|
|
680
683
|
if (req.headers['x-hrr-context']) return null; // harness manages its own context
|
|
@@ -684,7 +687,7 @@ async function maybeCacheCorpus(req, bodyBuf, log, stats) {
|
|
|
684
687
|
try {
|
|
685
688
|
const body = JSON.parse(bodyBuf.toString('utf8'));
|
|
686
689
|
if (Array.isArray(body?.messages) && body.messages.length) {
|
|
687
|
-
return spillTranscript(body, log, req, stats);
|
|
690
|
+
return spillTranscript(body, log, req, stats, extra);
|
|
688
691
|
}
|
|
689
692
|
} catch { /* not json */ }
|
|
690
693
|
return null;
|
|
@@ -701,11 +704,11 @@ async function maybeCacheCorpus(req, bodyBuf, log, stats) {
|
|
|
701
704
|
const oneShot = typeof last?.content === 'string'
|
|
702
705
|
&& last.content.length > BIND_MIN_CHARS
|
|
703
706
|
&& last.content.lastIndexOf('\n\n') >= BIND_MIN_CHARS;
|
|
704
|
-
if (!oneShot) return spillTranscript(body, log, req, stats);
|
|
707
|
+
if (!oneShot) return spillTranscript(body, log, req, stats, extra);
|
|
705
708
|
const cut = last.content.lastIndexOf('\n\n');
|
|
706
709
|
const corpus = last.content.slice(0, cut);
|
|
707
710
|
const ask = last.content.slice(cut + 2).trim();
|
|
708
|
-
if (!ask || ask.length > 8000) return spillTranscript(body, log, req, stats);
|
|
711
|
+
if (!ask || ask.length > 8000) return spillTranscript(body, log, req, stats, extra);
|
|
709
712
|
|
|
710
713
|
const bind = await bindCorpus(corpus, {
|
|
711
714
|
onStage: (stage, info) => {
|
|
@@ -1396,7 +1399,15 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
1396
1399
|
try {
|
|
1397
1400
|
// Spill/adapt diagnostics (adapt, file-stub, sending N/M) must hit
|
|
1398
1401
|
// ~/.openzoo/proxy.log when we are silent. `log` is a no-op then.
|
|
1399
|
-
cached = await maybeCacheCorpus(req, bodyBuf, say, spill
|
|
1402
|
+
cached = await maybeCacheCorpus(req, bodyBuf, say, spill, {
|
|
1403
|
+
lastSend: lastSpillSend?.sent,
|
|
1404
|
+
dollarX: hudDollarX({
|
|
1405
|
+
spillDirect: spill.spillDirect,
|
|
1406
|
+
spillSpend: spill.spillSpend,
|
|
1407
|
+
sessionDirect,
|
|
1408
|
+
sessionSpent,
|
|
1409
|
+
}),
|
|
1410
|
+
});
|
|
1400
1411
|
} catch (err) {
|
|
1401
1412
|
log(`context cache skipped for this call: ${err.message}`);
|
|
1402
1413
|
}
|
package/lib/spill.js
CHANGED
|
@@ -492,6 +492,38 @@ export function looksLikeFileView(command) {
|
|
|
492
492
|
return sawView;
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
+
function toolFnName(c) {
|
|
496
|
+
return c?.function?.name || c?.name || '';
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Read / Edit / Write (and file-view Bash). Bodies for these that remain
|
|
501
|
+
* after the last user ask must stay real — a `[bound, N chars]` placeholder
|
|
502
|
+
* in the live tail makes the model think Read is broken and cat files.
|
|
503
|
+
*/
|
|
504
|
+
export function isLiveFileTool(name, args = {}) {
|
|
505
|
+
const n = String(name || '');
|
|
506
|
+
if (/^(read|edit|write)/i.test(n)) return true;
|
|
507
|
+
if (/bash/i.test(n) && looksLikeFileView(args?.command)) return true;
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Same number the HUD green `x` uses: this-call / running spill
|
|
513
|
+
* direct÷billed, else session-wide. Null when no dollar figure exists.
|
|
514
|
+
*/
|
|
515
|
+
export function hudDollarX({
|
|
516
|
+
spillDirect, spillSpend, sessionDirect, sessionSpent,
|
|
517
|
+
} = {}) {
|
|
518
|
+
const billed = Number(spillSpend);
|
|
519
|
+
const direct = Number(spillDirect);
|
|
520
|
+
if (billed > 0 && Number.isFinite(direct)) return direct / billed;
|
|
521
|
+
const sessBilled = Number(sessionSpent);
|
|
522
|
+
const sessDirect = Number(sessionDirect);
|
|
523
|
+
if (sessBilled > 0 && Number.isFinite(sessDirect)) return sessDirect / sessBilled;
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
|
|
495
527
|
/** Tool result larger than this is "fat" — stub it in the forwarded tail. */
|
|
496
528
|
export const FAT_TOOL_CHARS = 400;
|
|
497
529
|
|
|
@@ -645,8 +677,13 @@ function resolveBoundPath(raw, cwd, boundAbs) {
|
|
|
645
677
|
* tool_calls / tool_use in that tail, plus tool results for those ids — not
|
|
646
678
|
* every Read/Bash/Search after the last user ask. In-flight latest bodies
|
|
647
679
|
* stay even when large. KEEP_TOOL_CHARS applies only to that last round so
|
|
648
|
-
* a 461-byte Read survives a follow-up ask.
|
|
649
|
-
*
|
|
680
|
+
* a 461-byte Read survives a follow-up ask.
|
|
681
|
+
*
|
|
682
|
+
* After the last user ask, remaining Read/Edit/Write / Bash-file bodies
|
|
683
|
+
* stay verbatim. Drop older post-ask rounds (trimRoundsAfterAsk) instead
|
|
684
|
+
* of rewriting them to `[bound, N chars]` — a placeholder in the live tail
|
|
685
|
+
* is 76-style blindness (model cats files). Fat WebSearch/Fetch in older
|
|
686
|
+
* kept rounds may still stub. stubMore tightens SEARCH, not live files.
|
|
650
687
|
*/
|
|
651
688
|
export function stubBoundFileResults(msgs, {
|
|
652
689
|
boundFiles,
|
|
@@ -675,12 +712,15 @@ export function stubBoundFileResults(msgs, {
|
|
|
675
712
|
}
|
|
676
713
|
|
|
677
714
|
const stubIds = new Set();
|
|
715
|
+
const fileIds = new Set();
|
|
716
|
+
const afterAskIds = new Set();
|
|
678
717
|
const idPaths = new Map();
|
|
679
718
|
let currentCwd = cwd;
|
|
680
719
|
|
|
681
720
|
const noteCall = (c) => {
|
|
682
721
|
const id = c?.id || c?.tool_call_id;
|
|
683
722
|
const args = parseArgs(c?.function?.arguments ?? c?.arguments);
|
|
723
|
+
if (id && isLiveFileTool(toolFnName(c), args)) fileIds.add(id);
|
|
684
724
|
const raws = [];
|
|
685
725
|
collectStructuredPaths(args, raws);
|
|
686
726
|
if (typeof args.command === 'string' && looksLikeFileView(args.command)) {
|
|
@@ -723,6 +763,15 @@ export function stubBoundFileResults(msgs, {
|
|
|
723
763
|
const slimArgs = aggressive || wantBudget;
|
|
724
764
|
let round = currentToolRound(msgs, { lastUser, fromIndex });
|
|
725
765
|
|
|
766
|
+
const markAfterAsk = (list) => {
|
|
767
|
+
afterAskIds.clear();
|
|
768
|
+
if (lastUser < 0 || !Array.isArray(list)) return;
|
|
769
|
+
for (let i = lastUser + 1; i < list.length; i++) {
|
|
770
|
+
for (const id of toolCallIds(list[i])) afterAskIds.add(id);
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
markAfterAsk(msgs);
|
|
774
|
+
|
|
726
775
|
const stubFor = (id, n) => {
|
|
727
776
|
const paths = idPaths.get(id);
|
|
728
777
|
return paths?.length ? fileBoundStub(paths, n) : toolResultStub(n);
|
|
@@ -734,6 +783,8 @@ export function stubBoundFileResults(msgs, {
|
|
|
734
783
|
if (id && round.inFlight && round.ids.has(id)) return false;
|
|
735
784
|
// Last completed batch (ask follows): keep small bodies only.
|
|
736
785
|
if (id && round.ids.has(id) && n < keepFloor) return false;
|
|
786
|
+
// Remaining post-ask file bodies stay real. Drop the round instead.
|
|
787
|
+
if (id && afterAskIds.has(id) && fileIds.has(id)) return false;
|
|
737
788
|
if (stubIds.has(id)) return true;
|
|
738
789
|
if (slimArgs && n >= fatLimit) return true;
|
|
739
790
|
if (overBudget && n > 0) return true;
|
|
@@ -762,6 +813,7 @@ export function stubBoundFileResults(msgs, {
|
|
|
762
813
|
firstSpillable: firstSpillableIndex(messages),
|
|
763
814
|
});
|
|
764
815
|
lastUser = lastUserAskIndex(messages, firstSpillableIndex(messages));
|
|
816
|
+
markAfterAsk(messages);
|
|
765
817
|
round = currentToolRound(messages, { lastUser, fromIndex });
|
|
766
818
|
}
|
|
767
819
|
|
|
@@ -877,9 +929,15 @@ export function stubBoundFileResults(msgs, {
|
|
|
877
929
|
messages = next;
|
|
878
930
|
used = sliceChars(messages, fromIndex);
|
|
879
931
|
if (used > cap) {
|
|
880
|
-
//
|
|
881
|
-
//
|
|
882
|
-
//
|
|
932
|
+
// Still over: drop older post-ask rounds rather than rewriting
|
|
933
|
+
// remaining file bodies to `[bound]`. Then trim an un-severable
|
|
934
|
+
// last chain. Latest batch stays.
|
|
935
|
+
messages = trimRoundsAfterAsk(messages, {
|
|
936
|
+
fromIndex,
|
|
937
|
+
keepTail: 1,
|
|
938
|
+
lastUser,
|
|
939
|
+
});
|
|
940
|
+
lastUser = lastUserAskIndex(messages, firstSpillableIndex(messages));
|
|
883
941
|
messages = trimUnseverablePairs(messages, {
|
|
884
942
|
fromIndex,
|
|
885
943
|
keepTail: 1,
|
|
@@ -895,6 +953,8 @@ export function stubBoundFileResults(msgs, {
|
|
|
895
953
|
|
|
896
954
|
export const ADAPT_TARGET = 10;
|
|
897
955
|
export const ADAPT_LOOSEN_AT = 20;
|
|
956
|
+
/** lastSend growing past this while dollar x < target is a tighten signal. */
|
|
957
|
+
export const LAST_SEND_TIGHTEN = 24;
|
|
898
958
|
|
|
899
959
|
export const KNOB_DEFAULTS = Object.freeze({
|
|
900
960
|
keepTail: 8,
|
|
@@ -1129,18 +1189,28 @@ function fmtRatio(ratio) {
|
|
|
1129
1189
|
return String(Number(ratio.toFixed(2)));
|
|
1130
1190
|
}
|
|
1131
1191
|
|
|
1132
|
-
function adaptLine({ action, ratio, knobs, target = ADAPT_TARGET }) {
|
|
1133
|
-
|
|
1134
|
-
|
|
1192
|
+
function adaptLine({ action, ratio, knobs, target = ADAPT_TARGET, dollarX } = {}) {
|
|
1193
|
+
const d = Number(dollarX);
|
|
1194
|
+
const hasD = Number.isFinite(d) && d > 0;
|
|
1195
|
+
const via = hasD
|
|
1196
|
+
? `dollar=${fmtRatio(d)} chars=${fmtRatio(ratio)}`
|
|
1197
|
+
: `ratio=${fmtRatio(ratio)}`;
|
|
1198
|
+
if (action === 'hold') return `adapt hold ${via}`;
|
|
1199
|
+
return `adapt ${via} target=${target} tail=${knobs.keepTail} budget=${knobs.budget}`;
|
|
1135
1200
|
}
|
|
1136
1201
|
|
|
1137
1202
|
/**
|
|
1138
|
-
* Decide whether to shrink, loosen, or hold.
|
|
1139
|
-
*
|
|
1140
|
-
*
|
|
1203
|
+
* Decide whether to shrink, loosen, or hold.
|
|
1204
|
+
*
|
|
1205
|
+
* Score the HUD dollar multiple (direct/billed) when present. Char ratio
|
|
1206
|
+
* is the fallback only. Tighten recuts this request. Loosen only when the
|
|
1207
|
+
* scored metric is above loosenAt AND last action was hold — never loosen
|
|
1208
|
+
* off a char-only overshoot while dollar x is in hand and below loosenAt.
|
|
1141
1209
|
*/
|
|
1142
1210
|
export function adaptTail({
|
|
1143
1211
|
ratio,
|
|
1212
|
+
dollarX,
|
|
1213
|
+
lastSend,
|
|
1144
1214
|
knobs,
|
|
1145
1215
|
lastAction = 'hold',
|
|
1146
1216
|
corpusChars,
|
|
@@ -1148,22 +1218,31 @@ export function adaptTail({
|
|
|
1148
1218
|
loosenAt = ADAPT_LOOSEN_AT,
|
|
1149
1219
|
} = {}) {
|
|
1150
1220
|
const cur = sanitizeKnobs(knobs);
|
|
1151
|
-
|
|
1152
|
-
|
|
1221
|
+
const dollar = Number(dollarX);
|
|
1222
|
+
const hasDollar = Number.isFinite(dollar) && dollar > 0;
|
|
1223
|
+
const score = hasDollar ? dollar : Number(ratio);
|
|
1224
|
+
const sendN = Number(lastSend);
|
|
1225
|
+
const sendGrowing = Number.isFinite(sendN) && sendN > LAST_SEND_TIGHTEN;
|
|
1226
|
+
const line = (action, knobsNow) => adaptLine({
|
|
1227
|
+
action, ratio, knobs: knobsNow, target, dollarX: hasDollar ? dollar : undefined,
|
|
1228
|
+
});
|
|
1229
|
+
if (!Number.isFinite(score)) {
|
|
1230
|
+
return { action: 'hold', knobs: cur, recut: false, ratio, dollarX: hasDollar ? dollar : null, score, log: line('hold', cur) };
|
|
1153
1231
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1232
|
+
// Dollar miss, or lastSend growing while dollar x is under target.
|
|
1233
|
+
if (score < target || (hasDollar && dollar < target && sendGrowing)) {
|
|
1234
|
+
const next = tightenKnobs(cur, { ratio: score, corpusChars, target });
|
|
1156
1235
|
const changed = !sameKnobs(next, cur);
|
|
1157
1236
|
const action = changed ? 'tighten' : 'hold';
|
|
1158
|
-
return { action, knobs: next, recut: changed, ratio,
|
|
1237
|
+
return { action, knobs: next, recut: changed, ratio, dollarX: hasDollar ? dollar : null, score, log: line(action, next) };
|
|
1159
1238
|
}
|
|
1160
|
-
if (
|
|
1239
|
+
if (score > loosenAt && lastAction === 'hold') {
|
|
1161
1240
|
const next = loosenKnobs(cur);
|
|
1162
1241
|
const changed = !sameKnobs(next, cur);
|
|
1163
1242
|
const action = changed ? 'loosen' : 'hold';
|
|
1164
|
-
return { action, knobs: next, recut: false, ratio,
|
|
1243
|
+
return { action, knobs: next, recut: false, ratio, dollarX: hasDollar ? dollar : null, score, log: line(action, next) };
|
|
1165
1244
|
}
|
|
1166
|
-
return { action: 'hold', knobs: cur, recut: false, ratio,
|
|
1245
|
+
return { action: 'hold', knobs: cur, recut: false, ratio, dollarX: hasDollar ? dollar : null, score, log: line('hold', cur) };
|
|
1167
1246
|
}
|
|
1168
1247
|
|
|
1169
1248
|
function firstSpillableIndex(msgs) {
|
|
@@ -1354,10 +1433,11 @@ function stubForCut(msgs, cut, opts) {
|
|
|
1354
1433
|
}
|
|
1355
1434
|
|
|
1356
1435
|
/**
|
|
1357
|
-
* Cut + stub, then retune knobs toward
|
|
1358
|
-
*
|
|
1359
|
-
*
|
|
1360
|
-
*
|
|
1436
|
+
* Cut + stub, then retune knobs toward the HUD dollar multiple (direct /
|
|
1437
|
+
* billed) when present, else corpus/sent. A miss recuts once this request
|
|
1438
|
+
* (smaller keepTail; stubMore for SEARCH, not live file bodies). A dollar
|
|
1439
|
+
* overshoot loosens one notch for the next request only (no flip-flop).
|
|
1440
|
+
* Env OPENZOO_ADAPT=0 disables the tuner; env still seeds the initial knobs.
|
|
1361
1441
|
*/
|
|
1362
1442
|
export function applySpillCut(msgs, {
|
|
1363
1443
|
knobs,
|
|
@@ -1370,6 +1450,8 @@ export function applySpillCut(msgs, {
|
|
|
1370
1450
|
persist = false,
|
|
1371
1451
|
file,
|
|
1372
1452
|
home,
|
|
1453
|
+
dollarX,
|
|
1454
|
+
lastSend,
|
|
1373
1455
|
} = {}) {
|
|
1374
1456
|
const persistOpts = { persist, file, home };
|
|
1375
1457
|
let k = sanitizeKnobs(knobs || getLiveKnobs(persistOpts));
|
|
@@ -1389,12 +1471,37 @@ export function applySpillCut(msgs, {
|
|
|
1389
1471
|
// No severable index — still stub/trim the un-severable tail so a
|
|
1390
1472
|
// 300-result storm is not forwarded at full size.
|
|
1391
1473
|
const stubFrom = plan.firstSpillable >= 0 ? plan.firstSpillable : 0;
|
|
1392
|
-
|
|
1474
|
+
let stubbed = stubForCut(msgs, stubFrom, {
|
|
1393
1475
|
boundFiles, boundAbs, cwd, aggressive: k.stubMore, budget: k.budget, keepTail: k.keepTail,
|
|
1394
1476
|
});
|
|
1395
|
-
|
|
1477
|
+
let sentChars = sliceChars(stubbed.messages, stubFrom);
|
|
1396
1478
|
const corpus = Math.max(Number(corpusChars) || 0, sentChars);
|
|
1397
|
-
|
|
1479
|
+
let ratio = spillRatio(corpus, sentChars);
|
|
1480
|
+
let action = 'hold';
|
|
1481
|
+
if (adapt) {
|
|
1482
|
+
const thisSend = Math.max(0, stubbed.messages.length - stubFrom);
|
|
1483
|
+
const decision = adaptTail({
|
|
1484
|
+
ratio,
|
|
1485
|
+
dollarX,
|
|
1486
|
+
lastSend: Math.max(Number(lastSend) || 0, thisSend),
|
|
1487
|
+
knobs: k,
|
|
1488
|
+
lastAction: lastAdaptAction,
|
|
1489
|
+
corpusChars: corpus,
|
|
1490
|
+
});
|
|
1491
|
+
k = decision.knobs;
|
|
1492
|
+
action = decision.action;
|
|
1493
|
+
if (decision.recut) {
|
|
1494
|
+
stubbed = stubForCut(msgs, stubFrom, {
|
|
1495
|
+
boundFiles, boundAbs, cwd, aggressive: k.stubMore, budget: k.budget, keepTail: k.keepTail,
|
|
1496
|
+
});
|
|
1497
|
+
sentChars = sliceChars(stubbed.messages, stubFrom);
|
|
1498
|
+
ratio = spillRatio(corpus, sentChars);
|
|
1499
|
+
}
|
|
1500
|
+
rememberKnobs(k, persistOpts);
|
|
1501
|
+
lastAdaptAction = action;
|
|
1502
|
+
log(adaptLine({ action, ratio, knobs: k, dollarX }));
|
|
1503
|
+
}
|
|
1504
|
+
return { ...empty, knobs: k, stubbed, sentChars, ratio, action };
|
|
1398
1505
|
}
|
|
1399
1506
|
|
|
1400
1507
|
const measure = (cut, stubbed, knobsNow) => {
|
|
@@ -1417,8 +1524,11 @@ export function applySpillCut(msgs, {
|
|
|
1417
1524
|
let action = 'hold';
|
|
1418
1525
|
|
|
1419
1526
|
if (adapt) {
|
|
1527
|
+
const thisSend = Math.max(0, stubbed.messages.length - plan.cut);
|
|
1420
1528
|
const decision = adaptTail({
|
|
1421
1529
|
ratio: stats.ratio,
|
|
1530
|
+
dollarX,
|
|
1531
|
+
lastSend: Math.max(Number(lastSend) || 0, thisSend),
|
|
1422
1532
|
knobs: k,
|
|
1423
1533
|
lastAction: lastAdaptAction,
|
|
1424
1534
|
corpusChars: stats.corpusChars,
|
|
@@ -1436,7 +1546,7 @@ export function applySpillCut(msgs, {
|
|
|
1436
1546
|
}
|
|
1437
1547
|
rememberKnobs(k, persistOpts);
|
|
1438
1548
|
lastAdaptAction = action;
|
|
1439
|
-
log(adaptLine({ action, ratio: stats.ratio, knobs: k }));
|
|
1549
|
+
log(adaptLine({ action, ratio: stats.ratio, knobs: k, dollarX }));
|
|
1440
1550
|
}
|
|
1441
1551
|
|
|
1442
1552
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.82",
|
|
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",
|