From 551eb8409a9a67988e74a22c3706f9ba74382864 Mon Sep 17 00:00:00 2001 From: wized2 Date: Mon, 14 Sep 2026 16:42:47 +0000 Subject: [PATCH 1/2] test: empty messages and no-op compact edge cases --- test/basic.test.mjs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/basic.test.mjs b/test/basic.test.mjs index 24f213e..11002c2 100644 --- a/test/basic.test.mjs +++ b/test/basic.test.mjs @@ -92,3 +92,31 @@ test("compact rejects invalid budgets before transforming the payload", () => { ); } }); + +test("analyzePayload handles empty messages and whitespace-only content", () => { + const empty = analyzePayload({ messages: [] }); + assert.equal(empty.totalTokens, 0); + + const whitespace = analyzePayload({ + messages: [{ role: "user", content: " \n\t " }], + }); + assert.ok(whitespace.totalTokens >= 0); + + const mixed = analyzePayload({ + system: "", + messages: [ + { role: "user", content: [] }, + { role: "assistant", content: "" }, + ], + }); + assert.ok(mixed.totalTokens >= 0); +}); + +test("compact is a no-op on already-small payloads", () => { + const payload = { + messages: [{ role: "user", content: "hi" }], + }; + const { payload: out, report } = compact(payload, { maxTokens: 10_000 }); + assert.equal(out.messages.length, 1); + assert.ok(report.afterTokens <= report.beforeTokens); +}); From d03b73c66520d8067cd023e1338e4956662c3170 Mon Sep 17 00:00:00 2001 From: Mudsir Wazir Date: Tue, 15 Sep 2026 00:17:50 +0500 Subject: [PATCH 2/2] fix: handle null payload in compact; cover system-only edge cases (#19) compact(null) no longer throws. Tests for null/undefined analyze, system-only payloads, and compact(null). Related to #2 Co-authored-by: wized2 Co-authored-by: royalpinto007 --- src/index.mjs | 6 ++++++ test/basic.test.mjs | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/index.mjs b/src/index.mjs index b4a1129..ecb07e4 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -87,6 +87,12 @@ export function compact(payload, opts = {}) { requireNonNegativeNumber("maxToolResultTokens", maxToolResultTokens); if (maxTokens != null) requireNonNegativeNumber("maxTokens", maxTokens); requireNonNegativeNumber("keepLastTurns", keepLastTurns, { integer: true }); + if (payload == null) { + return { + payload: { messages: [] }, + report: { beforeTokens: 0, afterTokens: 0, savedTokens: 0, savedPct: 0, actions: [] }, + }; + } const before = analyzePayload(payload, { counter }).totalTokens; const out = clone(payload); const actions = []; diff --git a/test/basic.test.mjs b/test/basic.test.mjs index b637be4..a9caa39 100644 --- a/test/basic.test.mjs +++ b/test/basic.test.mjs @@ -93,6 +93,32 @@ test("compact rejects invalid budgets before transforming the payload", () => { } }); + +test("analyzePayload null / undefined yields zero tokens", () => { + assert.equal(analyzePayload(null).totalTokens, 0); + assert.equal(analyzePayload(undefined).totalTokens, 0); +}); + +test("analyzePayload system-only object counts system text", () => { + const a = analyzePayload({ system: "You are a careful assistant." }); + assert.ok(a.totalTokens > 0); + assert.ok(a.byRole.system > 0); + assert.equal(a.byRole.user ?? 0, 0); +}); + +test("compact preserves system-only payload under a tight budget", () => { + const payload = { system: "sys", messages: [] }; + const { payload: out, report } = compact(payload, { maxTokens: 1, keepLastTurns: 0 }); + assert.ok(out.system === "sys" || (out.messages && out.messages[0]?.role === "system")); + assert.ok(report.afterTokens >= 0); +}); + +test("compact null payload returns empty messages without throwing", () => { + const { payload: out, report } = compact(null); + assert.deepEqual(out, { messages: [] }); + assert.equal(report.beforeTokens, 0); + assert.equal(report.afterTokens, 0); +}); test("analyzePayload empty messages yields zero tokens", () => { const a = analyzePayload({ messages: [] }); assert.equal(a.totalTokens, 0);