openzoo 0.48.32 → 0.48.33
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 -2
- package/package.json +1 -1
package/lib/proxy.js
CHANGED
|
@@ -300,9 +300,31 @@ async function spillTranscript(body, log) {
|
|
|
300
300
|
const firstSpillable = msgs.findIndex((m) => m?.role !== 'system');
|
|
301
301
|
if (firstSpillable < 0) return null;
|
|
302
302
|
|
|
303
|
+
// A SEVERABLE BOUNDARY IS NOT ONLY A `user` TURN.
|
|
304
|
+
//
|
|
305
|
+
// That was the whole bug: in a Claude Code agent loop the human speaks ONCE
|
|
306
|
+
// and everything after is assistant->tool pairs, so a rule that only cuts at
|
|
307
|
+
// role:"user" finds nothing and the spill returns null on EVERY turn.
|
|
308
|
+
// MEASURED against the live proxy log: 104 real claude-cli requests, zero
|
|
309
|
+
// spills, while every synthetic transcript I tested spilled fine because I
|
|
310
|
+
// had written user turns into it.
|
|
311
|
+
//
|
|
312
|
+
// What actually matters is that no assistant `tool_calls` is left unanswered
|
|
313
|
+
// across the cut. So any message is a legal boundary when the one BEFORE it
|
|
314
|
+
// is not an unanswered tool call — i.e. the previous message is a plain
|
|
315
|
+
// assistant/user/tool with every call already resolved.
|
|
316
|
+
const severable = (i) => {
|
|
317
|
+
if (i <= firstSpillable || i >= msgs.length) return false;
|
|
318
|
+
const prev = msgs[i - 1];
|
|
319
|
+
if (!prev) return false;
|
|
320
|
+
// an assistant that issued tool_calls must be followed by its tool results
|
|
321
|
+
if (prev.role === 'assistant' && Array.isArray(prev.tool_calls) && prev.tool_calls.length) return false;
|
|
322
|
+
// never split a tool_call/tool_result run in the middle
|
|
323
|
+
return msgs[i].role !== 'tool';
|
|
324
|
+
};
|
|
303
325
|
let cut = -1;
|
|
304
326
|
for (let i = msgs.length - keepTail; i > firstSpillable; i--) {
|
|
305
|
-
if (
|
|
327
|
+
if (severable(i)) { cut = i; break; }
|
|
306
328
|
}
|
|
307
329
|
// FALL BACK TO THE LAST SEVERABLE TURN. The keepTail window is a preference,
|
|
308
330
|
// not a requirement: a transcript can be enormous and still have very few
|
|
@@ -312,7 +334,7 @@ async function spillTranscript(body, log) {
|
|
|
312
334
|
// final turn; anything earlier that is severable is better than not spilling.
|
|
313
335
|
if (cut <= firstSpillable) {
|
|
314
336
|
for (let i = msgs.length - 2; i > firstSpillable; i--) {
|
|
315
|
-
if (
|
|
337
|
+
if (severable(i)) { cut = i; break; }
|
|
316
338
|
}
|
|
317
339
|
}
|
|
318
340
|
if (cut <= firstSpillable) return null; // nothing safely severable
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.33",
|
|
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",
|