import test from 'brittle' import { readFileSync } from 'node:fs' import vm from 'node:vm' const STATE_SRC = readFileSync(new URL('../lib/agent-state.js', import.meta.url), 'utf8') const COMPACT_SRC = readFileSync( new URL('../lib/agent-compact.js', import.meta.url), 'utf8' ) function load() { const sandbox = { console, TextEncoder, TextDecoder } vm.createContext(sandbox) vm.runInContext(STATE_SRC, sandbox) vm.runInContext(COMPACT_SRC, sandbox) return sandbox } test('trim for ctx shrinks system when tools consume budget', async (t) => { const { bareAgentTrimMessagesForCtx, bareAgentEstimateTokens } = load() const tools = [{ name: 'x', description: 'y'.repeat(8000), parameters: {} }] const msgs = [ { role: 'system', content: 'S'.repeat(20_000) }, { role: 'user', content: 'hello' } ] const out = bareAgentTrimMessagesForCtx(msgs, 4096, { tools, reserveCompletion: 512 }) t.is(out.length, 2) t.ok(String(/** @type {any} */ (out[0]).content).length < 20_000) t.ok( bareAgentEstimateTokens(out) + bareAgentEstimateTokens(tools) + 512 <= 4096 + 200 ) }) test('advanced compaction summarizes older turns instead of dropping blindly', async (t) => { const { bareAgentCompactMessagesForCtx, bareAgentIsCompactionMessage } = load() /** @type {unknown[]} */ const msgs = [{ role: 'system', content: 'You are Bare OS agent.' }] for (let i = 0; i < 20; i++) { msgs.push({ role: 'user', content: 'Please inspect /tmp/file-' + i + ' and report status code ' + i }) msgs.push({ role: 'assistant', content: null, tool_calls: [ { id: 'c' + i, type: 'function', function: { name: 'run_command', arguments: JSON.stringify({ command: 'cat /tmp/file-' + i }) } } ] }) msgs.push({ role: 'tool', tool_call_id: 'c' + i, name: 'run_command', content: 'EXIT:0\n' + ('payload-' + i + '-').repeat(400) }) msgs.push({ role: 'assistant', content: 'File ' + i + ' looks fine with exit 0.' }) } msgs.push({ role: 'user', content: 'What did we learn about the last files?' }) const packed = bareAgentCompactMessagesForCtx(msgs, 4096, { reserveCompletion: 256, mode: 'aggressive', keepRecent: 3, toolMaxChars: 400, summaryMaxChars: 1800 }) t.ok(packed.meta.afterTokens <= packed.meta.budget + 80) t.ok(packed.meta.compacted) t.ok( packed.meta.droppedGroups > 0 || packed.meta.tiers.includes('rolling-summary') || packed.meta.tiers.some((x) => String(x).startsWith('tighten-keep')) ) t.ok(packed.messages.some((m) => bareAgentIsCompactionMessage(m))) // Recent user question survives. const last = packed.messages[packed.messages.length - 1] t.ok( String(/** @type {any} */ (last).content || '').includes('last files') ) // Summary mentions tools or paths. const summary = packed.messages.find((m) => bareAgentIsCompactionMessage(m)) t.ok(summary) t.ok( /run_command|\/tmp\/file|assistant|tool:/i.test( String(/** @type {any} */ (summary).content || '') ) ) }) test('compaction mode off falls back to hard trim only', async (t) => { const { bareAgentCompactMessagesForCtx } = load() const msgs = [ { role: 'system', content: 'S'.repeat(5000) }, { role: 'user', content: 'a' }, { role: 'assistant', content: 'b'.repeat(5000) }, { role: 'user', content: 'c' } ] const packed = bareAgentCompactMessagesForCtx(msgs, 2048, { mode: 'off', reserveCompletion: 256 }) t.is(packed.meta.mode, 'off') t.ok(packed.meta.afterTokens <= packed.meta.budget + 100) }) test('fat tool payloads shrink even under soft budget', async (t) => { const { bareAgentCompactMessagesForCtx, bareAgentEstimateTokens } = load() const msgs = [ { role: 'system', content: 'sys' }, { role: 'user', content: 'run it' }, { role: 'tool', name: 'run_command', content: 'X'.repeat(20_000) } ] const before = bareAgentEstimateTokens(msgs) const packed = bareAgentCompactMessagesForCtx(msgs, 32768, { mode: 'auto', reserveCompletion: 512, toolMaxChars: 1000 }) const tool = /** @type {any} */ ( packed.messages.find( (m) => m && /** @type {any} */ (m).role === 'tool' ) ) t.ok(tool) t.ok(String(tool.content).length < 20_000) t.ok(packed.meta.afterTokens < before) t.ok(packed.meta.tiers.includes('shrink-tools') || packed.meta.compacted) })