Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/services/compact/microCompact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,65 @@ describe('microCompact MCP tool compaction', () => {
})
})
})

describe('estimateMessageTokens coverage', () => {
test('counts tokens from string content user messages', async () => {
const { estimateMessageTokens } = await import('./microCompact.js')

// Real user messages from the session can have string content (plain user prompt)
const content =
'Hello world this is a long user prompt that needs compression because it is over the threshold'
const messages: Message[] = [createUserMessage({ content })]

const total = estimateMessageTokens(messages)
// The string-content user message should be counted on its own; the old
// implementation returned 0 because it skipped non-array content.
expect(total).toBe(Math.ceil(Math.round(content.length / 4) * (4 / 3)))
})

test('counts tokens across all block types including tool_result and tool_use', async () => {
const { estimateMessageTokens } = await import('./microCompact.js')

const msg = createUserMessage({
content: [
{ type: 'tool_result' as const, tool_use_id: 't1', content: 'A'.repeat(5000) },
],
})
const msgWithText = createUserMessage({
content: [
{ type: 'text' as const, text: 'User query here' },
],
})
const asstTool = createAssistantMessage({
content: [
{
type: 'tool_use' as const,
id: 't1',
name: 'Bash',
input: { command: 'ls -la' },
},
],
})
const msgWithResultAndText = createUserMessage({
content: [
{ type: 'tool_result' as const, tool_use_id: 't1', content: 'file output data' },
{ type: 'text' as const, text: 'based on that output I think the answer is yes' },
],
})

const messages: Message[] = [msg, msgWithText, asstTool, msgWithResultAndText]

const total = estimateMessageTokens(messages)

// Must be significantly > 0 (all blocks counted)
expect(total).toBeGreaterThan(100)

// Must be larger than the old reducer's estimate (only string+text blocks)
// Old reducer would only see text blocks: 'User query here' + 'based on that output...'
// It would miss 5000-chars tool_result, tool_use name+input
const oldReducerEstimate = Math.ceil(
(13 + 46) * (4 / 3), // 'User query here' (13) + 'based on that...' (~46)
)
expect(total).toBeGreaterThan(oldReducerEstimate * 2)
})
})
5 changes: 5 additions & 0 deletions src/services/compact/microCompact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ export function estimateMessageTokens(messages: Message[]): number {
continue
}

if (typeof message.message?.content === 'string') {
totalTokens += roughTokenCountEstimation(message.message.content)
continue
}

if (!Array.isArray(message.message.content)) {
continue
}
Expand Down