From 87c5e5439144ddcd03dc0f5f2bafc2e087e6db46 Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 19:32:44 +0800 Subject: [PATCH 1/7] feat(agent-core): record context length before and after micro compaction --- .../record-micro-compaction-context-length.md | 6 ++++++ packages/agent-core/src/agent/compaction/micro.ts | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .changeset/record-micro-compaction-context-length.md diff --git a/.changeset/record-micro-compaction-context-length.md b/.changeset/record-micro-compaction-context-length.md new file mode 100644 index 000000000..412a15272 --- /dev/null +++ b/.changeset/record-micro-compaction-context-length.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": patch +"@moonshot-ai/kimi-code": patch +--- + +Record context token length before and after micro compaction. diff --git a/packages/agent-core/src/agent/compaction/micro.ts b/packages/agent-core/src/agent/compaction/micro.ts index da352db69..e08e5e9ce 100644 --- a/packages/agent-core/src/agent/compaction/micro.ts +++ b/packages/agent-core/src/agent/compaction/micro.ts @@ -2,7 +2,10 @@ import type { ContentPart } from '@moonshot-ai/kosong'; import type { Agent } from '..'; import type { ContextMessage } from '../context'; -import { estimateTokensForContentParts } from '../../utils/tokens'; +import { + estimateTokensForContentParts, + estimateTokensForMessages, +} from '../../utils/tokens'; export interface MicroCompactionConfig { keepRecentMessages: number; @@ -65,9 +68,17 @@ export class MicroCompaction { this.apply(nextCutoff); if (previousCutoff !== nextCutoff) { const effect = this.measureEffect(history, nextCutoff); + // Whole-context length before/after trimming, mirroring the + // `tokensBefore`/`tokensAfter` fields on `compaction_finished` so the + // two compaction paths can be compared on the same axis. + const contextTokensBefore = estimateTokensForMessages(history); + const contextTokensAfter = + contextTokensBefore - effect.beforeTokens + effect.afterTokens; this.agent.telemetry.track('micro_compaction_applied', { ...config, ...effect, + contextTokensBefore, + contextTokensAfter, previous_cutoff: previousCutoff, cutoff: nextCutoff, message_count: history.length, From 9bf5c530d6546d6782b8a006846c881a1811a309 Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 19:33:49 +0800 Subject: [PATCH 2/7] test(agent-core): assert context length fields in micro compaction telemetry --- packages/agent-core/test/agent/compaction/micro.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/agent-core/test/agent/compaction/micro.test.ts b/packages/agent-core/test/agent/compaction/micro.test.ts index bcdeb327e..033333188 100644 --- a/packages/agent-core/test/agent/compaction/micro.test.ts +++ b/packages/agent-core/test/agent/compaction/micro.test.ts @@ -480,10 +480,15 @@ describe('MicroCompaction', () => { truncatedToolResultCount: 2, beforeTokens: expect.any(Number), afterTokens: expect.any(Number), + contextTokensBefore: expect.any(Number), + contextTokensAfter: expect.any(Number), }); expect(numberProperty(event, 'beforeTokens')).toBeGreaterThan( numberProperty(event, 'afterTokens'), ); + expect(numberProperty(event, 'contextTokensBefore')).toBeGreaterThan( + numberProperty(event, 'contextTokensAfter'), + ); expect(ctx.agent.context.messages).toHaveLength(9); expect(records.filter((record) => record.event === 'micro_compaction_applied')).toHaveLength(1); From 608a811648e9c59a001ae11b12294d7b1b0687b6 Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 20:12:57 +0800 Subject: [PATCH 3/7] chore: drop changeset for telemetry-only change --- .changeset/record-micro-compaction-context-length.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .changeset/record-micro-compaction-context-length.md diff --git a/.changeset/record-micro-compaction-context-length.md b/.changeset/record-micro-compaction-context-length.md deleted file mode 100644 index 412a15272..000000000 --- a/.changeset/record-micro-compaction-context-length.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@moonshot-ai/agent-core": patch -"@moonshot-ai/kimi-code": patch ---- - -Record context token length before and after micro compaction. From ed3f2272b6cd7c3c3b03e8c8e0f0e3556eef7778 Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 19:57:49 +0800 Subject: [PATCH 4/7] refactor(agent-core): rename micro compaction telemetry event to micro_compaction_finished --- .changeset/rename-micro-compaction-finished-event.md | 6 ++++++ packages/agent-core/src/agent/compaction/micro.ts | 2 +- packages/agent-core/test/agent/compaction/micro.test.ts | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 .changeset/rename-micro-compaction-finished-event.md diff --git a/.changeset/rename-micro-compaction-finished-event.md b/.changeset/rename-micro-compaction-finished-event.md new file mode 100644 index 000000000..3d952de12 --- /dev/null +++ b/.changeset/rename-micro-compaction-finished-event.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": minor +"@moonshot-ai/kimi-code": minor +--- + +Rename the micro compaction telemetry event to `micro_compaction_finished`, matching the finished/failed convention used by full compaction. diff --git a/packages/agent-core/src/agent/compaction/micro.ts b/packages/agent-core/src/agent/compaction/micro.ts index e08e5e9ce..d23494ad9 100644 --- a/packages/agent-core/src/agent/compaction/micro.ts +++ b/packages/agent-core/src/agent/compaction/micro.ts @@ -74,7 +74,7 @@ export class MicroCompaction { const contextTokensBefore = estimateTokensForMessages(history); const contextTokensAfter = contextTokensBefore - effect.beforeTokens + effect.afterTokens; - this.agent.telemetry.track('micro_compaction_applied', { + this.agent.telemetry.track('micro_compaction_finished', { ...config, ...effect, contextTokensBefore, diff --git a/packages/agent-core/test/agent/compaction/micro.test.ts b/packages/agent-core/test/agent/compaction/micro.test.ts index 033333188..fe50e3dfe 100644 --- a/packages/agent-core/test/agent/compaction/micro.test.ts +++ b/packages/agent-core/test/agent/compaction/micro.test.ts @@ -469,7 +469,7 @@ describe('MicroCompaction', () => { 'result three', ]); - const event = singleTelemetryEvent(records, 'micro_compaction_applied'); + const event = singleTelemetryEvent(records, 'micro_compaction_finished'); expect(event.properties).toMatchObject({ ...microCompaction, truncatedMarker: DEFAULT_MARKER, @@ -491,7 +491,7 @@ describe('MicroCompaction', () => { ); expect(ctx.agent.context.messages).toHaveLength(9); - expect(records.filter((record) => record.event === 'micro_compaction_applied')).toHaveLength(1); + expect(records.filter((record) => record.event === 'micro_compaction_finished')).toHaveLength(1); }); it('leaves context unchanged when the micro_compaction flag is disabled', () => { From 4b7330472253b7369c7def06e8099c6e4287d4a1 Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 20:06:44 +0800 Subject: [PATCH 5/7] fix(agent-core): align micro compaction context telemetry --- .../agent-core/src/agent/compaction/micro.ts | 9 ++-- .../test/agent/compaction/micro.test.ts | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/agent-core/src/agent/compaction/micro.ts b/packages/agent-core/src/agent/compaction/micro.ts index d23494ad9..382656cc6 100644 --- a/packages/agent-core/src/agent/compaction/micro.ts +++ b/packages/agent-core/src/agent/compaction/micro.ts @@ -68,12 +68,15 @@ export class MicroCompaction { this.apply(nextCutoff); if (previousCutoff !== nextCutoff) { const effect = this.measureEffect(history, nextCutoff); - // Whole-context length before/after trimming, mirroring the + const previousEffect = this.measureEffect(history, previousCutoff); + const rawContextTokens = estimateTokensForMessages(history); + // Whole-context length before/after this cutoff change, mirroring the // `tokensBefore`/`tokensAfter` fields on `compaction_finished` so the // two compaction paths can be compared on the same axis. - const contextTokensBefore = estimateTokensForMessages(history); + const contextTokensBefore = + rawContextTokens - previousEffect.beforeTokens + previousEffect.afterTokens; const contextTokensAfter = - contextTokensBefore - effect.beforeTokens + effect.afterTokens; + rawContextTokens - effect.beforeTokens + effect.afterTokens; this.agent.telemetry.track('micro_compaction_finished', { ...config, ...effect, diff --git a/packages/agent-core/test/agent/compaction/micro.test.ts b/packages/agent-core/test/agent/compaction/micro.test.ts index fe50e3dfe..d3eec9563 100644 --- a/packages/agent-core/test/agent/compaction/micro.test.ts +++ b/packages/agent-core/test/agent/compaction/micro.test.ts @@ -494,6 +494,50 @@ describe('MicroCompaction', () => { expect(records.filter((record) => record.event === 'micro_compaction_finished')).toHaveLength(1); }); + it('reports context token deltas from the previously compacted projection', () => { + vi.useFakeTimers(); + const records: TelemetryRecord[] = []; + const microCompaction = { + keepRecentMessages: 2, + minContentTokens: 1, + cacheMissedThresholdMs: 60 * MINUTE, + minContextUsageRatio: 0, + }; + const ctx = testAgent({ + telemetry: recordingTelemetry(records), + microCompaction, + }); + + vi.setSystemTime(0); + appendMicroToolExchange(ctx, 1, { output: 'result one '.repeat(20) }); + appendMicroToolExchange(ctx, 2, { output: 'result two '.repeat(20) }); + + vi.setSystemTime(61 * MINUTE); + ctx.agent.microCompaction.detect(); + expect(toolTexts(ctx.agent.context.messages)).toEqual([ + DEFAULT_MARKER, + 'result two '.repeat(20), + ]); + + vi.setSystemTime(62 * MINUTE); + appendMicroToolExchange(ctx, 3, { output: 'result three' }); + const expectedContextTokensBefore = estimateTokensForMessages(ctx.agent.context.messages); + + vi.setSystemTime(123 * MINUTE); + ctx.agent.microCompaction.detect(); + + const events = records.filter((record) => record.event === 'micro_compaction_finished'); + expect(events).toHaveLength(2); + const secondEvent = events[1]!; + expect(secondEvent.properties).toMatchObject({ + previous_cutoff: 4, + cutoff: 7, + truncatedToolResultCount: 2, + contextTokensBefore: expectedContextTokensBefore, + contextTokensAfter: estimateTokensForMessages(ctx.agent.context.messages), + }); + }); + it('leaves context unchanged when the micro_compaction flag is disabled', () => { vi.stubEnv(MICRO_COMPACTION_FLAG_ENV, '0'); vi.useFakeTimers(); From d7288f394e6f7f026884608518dcced5157b25df Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 20:13:07 +0800 Subject: [PATCH 6/7] chore: drop changeset for telemetry-only change --- .changeset/rename-micro-compaction-finished-event.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .changeset/rename-micro-compaction-finished-event.md diff --git a/.changeset/rename-micro-compaction-finished-event.md b/.changeset/rename-micro-compaction-finished-event.md deleted file mode 100644 index 3d952de12..000000000 --- a/.changeset/rename-micro-compaction-finished-event.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@moonshot-ai/agent-core": minor -"@moonshot-ai/kimi-code": minor ---- - -Rename the micro compaction telemetry event to `micro_compaction_finished`, matching the finished/failed convention used by full compaction. From 34461b8805a184e0de2cec65a77877aec44ffb80 Mon Sep 17 00:00:00 2001 From: 7Sageer <7sageer@djwcb.cn> Date: Wed, 17 Jun 2026 20:23:34 +0800 Subject: [PATCH 7/7] refactor(agent-core): align micro compaction token telemetry fields --- .../agent-core/src/agent/compaction/micro.ts | 30 ++++++++++++------- .../test/agent/compaction/micro.test.ts | 20 ++++++------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/agent-core/src/agent/compaction/micro.ts b/packages/agent-core/src/agent/compaction/micro.ts index 382656cc6..65a045a3d 100644 --- a/packages/agent-core/src/agent/compaction/micro.ts +++ b/packages/agent-core/src/agent/compaction/micro.ts @@ -73,15 +73,19 @@ export class MicroCompaction { // Whole-context length before/after this cutoff change, mirroring the // `tokensBefore`/`tokensAfter` fields on `compaction_finished` so the // two compaction paths can be compared on the same axis. - const contextTokensBefore = - rawContextTokens - previousEffect.beforeTokens + previousEffect.afterTokens; - const contextTokensAfter = - rawContextTokens - effect.beforeTokens + effect.afterTokens; + const tokensBefore = + rawContextTokens - + previousEffect.truncatedToolResultTokensBefore + + previousEffect.truncatedToolResultTokensAfter; + const tokensAfter = + rawContextTokens - + effect.truncatedToolResultTokensBefore + + effect.truncatedToolResultTokensAfter; this.agent.telemetry.track('micro_compaction_finished', { ...config, ...effect, - contextTokensBefore, - contextTokensAfter, + tokensBefore, + tokensAfter, previous_cutoff: previousCutoff, cutoff: nextCutoff, message_count: history.length, @@ -121,8 +125,8 @@ export class MicroCompaction { ) { let markerTokenCount: number | undefined; let truncatedToolResultCount = 0; - let beforeTokens = 0; - let afterTokens = 0; + let truncatedToolResultTokensBefore = 0; + let truncatedToolResultTokensAfter = 0; for (let i = 0; i < messages.length && i < cutoff; i++) { const message = messages[i]; if (message?.role !== 'tool' || message.toolCallId === undefined) continue; @@ -134,9 +138,13 @@ export class MicroCompaction { { type: 'text', text: this.config.truncatedMarker }, ]); truncatedToolResultCount += 1; - beforeTokens += contentTokens; - afterTokens += markerTokenCount; + truncatedToolResultTokensBefore += contentTokens; + truncatedToolResultTokensAfter += markerTokenCount; } - return { truncatedToolResultCount, beforeTokens, afterTokens }; + return { + truncatedToolResultCount, + truncatedToolResultTokensBefore, + truncatedToolResultTokensAfter, + }; } } diff --git a/packages/agent-core/test/agent/compaction/micro.test.ts b/packages/agent-core/test/agent/compaction/micro.test.ts index d3eec9563..34254d58c 100644 --- a/packages/agent-core/test/agent/compaction/micro.test.ts +++ b/packages/agent-core/test/agent/compaction/micro.test.ts @@ -478,16 +478,16 @@ describe('MicroCompaction', () => { message_count: 9, cache_age_ms: 61 * MINUTE, truncatedToolResultCount: 2, - beforeTokens: expect.any(Number), - afterTokens: expect.any(Number), - contextTokensBefore: expect.any(Number), - contextTokensAfter: expect.any(Number), + truncatedToolResultTokensBefore: expect.any(Number), + truncatedToolResultTokensAfter: expect.any(Number), + tokensBefore: expect.any(Number), + tokensAfter: expect.any(Number), }); - expect(numberProperty(event, 'beforeTokens')).toBeGreaterThan( - numberProperty(event, 'afterTokens'), + expect(numberProperty(event, 'truncatedToolResultTokensBefore')).toBeGreaterThan( + numberProperty(event, 'truncatedToolResultTokensAfter'), ); - expect(numberProperty(event, 'contextTokensBefore')).toBeGreaterThan( - numberProperty(event, 'contextTokensAfter'), + expect(numberProperty(event, 'tokensBefore')).toBeGreaterThan( + numberProperty(event, 'tokensAfter'), ); expect(ctx.agent.context.messages).toHaveLength(9); @@ -533,8 +533,8 @@ describe('MicroCompaction', () => { previous_cutoff: 4, cutoff: 7, truncatedToolResultCount: 2, - contextTokensBefore: expectedContextTokensBefore, - contextTokensAfter: estimateTokensForMessages(ctx.agent.context.messages), + tokensBefore: expectedContextTokensBefore, + tokensAfter: estimateTokensForMessages(ctx.agent.context.messages), }); });