[eric] router: small tool results age past the newest 30, which was a third of what a 735-call chat still carried

This commit is contained in:
ciregenz
2026-09-01 14:39:25 -07:00
parent 3184d9b78a
commit b683d88c34
2 changed files with 36 additions and 2 deletions
+21 -2
View File
@@ -24,6 +24,13 @@
const KEEP_RECENT = 5;
const MIN_STUB_BYTES = 2000;
// Small results are sacred while they are RECENT ("nothing to commit" IS the answer), but a
// 735-tool chat carries hundreds of them and they became the floor: measured on the real thrashing
// chat, small results were 32.0% (91,407 tokens) of everything the pruner left, more than any other
// bucket. A status line from four hundred turns ago is stale state, not an answer. Newest
// SMALL_KEEP_RECENT stay untouched; older ones collapse to a one-line stub that keeps nothing to
// re-run because the tool_use above them already carries the command.
const SMALL_KEEP_RECENT = 30;
const ENGAGE_BYTES = 300000;
// The CEILING, which is the property hermes has and an age-based cut does not. Clearing only OLD
// results is a discount: measured on five real sessions it took 1.6M tokens to 422K, but the
@@ -92,17 +99,29 @@ function pruneBody(bodyStr) {
try { data = JSON.parse(bodyStr); } catch (_) { return none; }
if (!data || !Array.isArray(data.messages)) return none;
// Collect every prunable tool_result in transcript order.
// Collect every prunable tool_result in transcript order; small ones age on their own track.
const candidates = [];
const smalls = [];
for (const msg of data.messages) {
if (!msg || msg.role !== 'user' || !Array.isArray(msg.content)) continue;
for (const block of msg.content) {
if (!block || block.type !== 'tool_result') continue;
const size = blockText(block).length;
if (size >= MIN_STUB_BYTES || blockHasImage(block)) candidates.push(block);
else if (size > 80) smalls.push(block);
}
}
if (candidates.length <= KEEP_RECENT) return none;
const smallKeepFrom = Math.max(0, smalls.length - SMALL_KEEP_RECENT);
for (let i = 0; i < smallKeepFrom; i++) {
smalls[i].content = [{ type: 'text', text: '[Old result cleared by OpenSwarm: '
+ blockText(smalls[i]).length + ' characters from an earlier step.]' }];
}
if (candidates.length <= KEEP_RECENT && smallKeepFrom === 0) return none;
if (candidates.length <= KEEP_RECENT) {
let outSmall;
try { outSmall = JSON.stringify(data); } catch (_) { return none; }
return { body: outSmall, stats: { stubbed: smallKeepFrom, kept: KEEP_RECENT, savedBytes: bodyStr.length - outSmall.length } };
}
const keepFrom = candidates.length - KEEP_RECENT;
const seenNewestFirst = new Set();
+15
View File
@@ -210,3 +210,18 @@ test('a small input is never touched, and neither is a command', () => {
assert.match(m.content[0].input.command, /^grep -rn/, 'a command is identity, never bulk');
}
});
test('old SMALL results age too, newest stay sacred (the 32% floor from the real thrashing chat)', () => {
const msgs = [{ role: 'user', content: 'p'.repeat(ENGAGE_BYTES) }];
for (let i = 0; i < 45; i++) {
msgs.push({ role: 'user', content: [toolResult('s' + i, `ok ${i}: nothing to commit, working tree clean padded ${'x'.repeat(120)}`)] });
}
const s = JSON.stringify({ model: 'claude-sonnet-4-6', messages: msgs });
const d = JSON.parse(pruneBody(s).body);
const res = d.messages.slice(1).map((m) => m.content[0]);
const cleared = res.filter((b) => /Old result cleared/.test(b.content[0].text)).length;
assert.strictEqual(cleared, 15, '45 smalls, newest 30 kept, oldest 15 aged');
for (let i = 45 - 30; i < 45; i++) {
assert.match(res[i].content[0].text, /nothing to commit/, 'a recent small IS the answer and must survive');
}
});