@@ -13,15 +13,39 @@ function buildMessages(count: number): FormattedMessage[] {
1313 } ) ) ;
1414}
1515
16+ function createAnthropicMock ( summary = 'compact summary' ) : Anthropic {
17+ return {
18+ messages : {
19+ create : vi . fn ( async ( ) => ( {
20+ content : [ { type : 'text' , text : summary } ] ,
21+ } ) ) ,
22+ } ,
23+ } as unknown as Anthropic ;
24+ }
25+
1626describe ( 'ContextCompressor' , ( ) => {
17- it ( 'needsCompression uses the 80% threshold ' , ( ) => {
18- const compressor = new ContextCompressor ( { } as Anthropic ) ;
27+ it ( 'returns false for null utilization ' , ( ) => {
28+ const compressor = new ContextCompressor ( createAnthropicMock ( ) ) ;
1929
2030 expect ( compressor . needsCompression ( null ) ) . toBe ( false ) ;
31+ } ) ;
32+
33+ it ( 'returns false below 80% utilization' , ( ) => {
34+ const compressor = new ContextCompressor ( createAnthropicMock ( ) ) ;
35+
2136 expect ( compressor . needsCompression ( 0.79 ) ) . toBe ( false ) ;
37+ } ) ;
38+
39+ it ( 'returns true at 80% utilization' , ( ) => {
40+ const compressor = new ContextCompressor ( createAnthropicMock ( ) ) ;
41+
2242 expect ( compressor . needsCompression ( 0.8 ) ) . toBe ( true ) ;
23- expect ( compressor . needsCompression ( 0.85 ) ) . toBe ( true ) ;
24- expect ( compressor . needsCompression ( 1.0 ) ) . toBe ( true ) ;
43+ } ) ;
44+
45+ it ( 'returns true above 80% utilization' , ( ) => {
46+ const compressor = new ContextCompressor ( createAnthropicMock ( ) ) ;
47+
48+ expect ( compressor . needsCompression ( 0.95 ) ) . toBe ( true ) ;
2549 } ) ;
2650
2751 it ( 'compress keeps the most recent 20% and summarizes the rest' , async ( ) => {
@@ -58,6 +82,22 @@ describe('ContextCompressor', () => {
5882 ) ;
5983 } ) ;
6084
85+ it ( 'compress keeps a single message and skips summarization' , async ( ) => {
86+ const anthropic = createAnthropicMock ( ) ;
87+ const compressor = new ContextCompressor ( anthropic ) ;
88+
89+ const result = await compressor . compress ( [
90+ { sender : 'User' , content : 'one' } ,
91+ ] ) ;
92+
93+ expect ( result ) . toEqual ( {
94+ summary : '' ,
95+ messagesCompressed : 0 ,
96+ messagesKept : 1 ,
97+ } ) ;
98+ expect ( anthropic . messages . create ) . not . toHaveBeenCalled ( ) ;
99+ } ) ;
100+
61101 it ( 'formatSummaryBlock wraps and escapes summary content' , ( ) => {
62102 const compressor = new ContextCompressor ( { } as Anthropic ) ;
63103
@@ -71,4 +111,20 @@ describe('ContextCompressor', () => {
71111 expect ( block ) . toContain ( 'Use <tags> & "quotes"' ) ;
72112 expect ( block ) . toContain ( '</context_summary>' ) ;
73113 } ) ;
114+
115+ it ( 'calls Haiku with the expected model' , async ( ) => {
116+ const anthropic = createAnthropicMock ( ) ;
117+ const compressor = new ContextCompressor ( anthropic ) ;
118+
119+ await compressor . compress ( [
120+ { sender : 'user' , content : 'old context' } ,
121+ { sender : 'assistant' , content : 'new context' } ,
122+ ] ) ;
123+
124+ expect ( anthropic . messages . create ) . toHaveBeenCalledWith (
125+ expect . objectContaining ( {
126+ model : 'claude-haiku-4-5-20251001' ,
127+ } ) ,
128+ ) ;
129+ } ) ;
74130} ) ;
0 commit comments