diff --git a/.changeset/fix-compaction-max-output-size.md b/.changeset/fix-compaction-max-output-size.md new file mode 100644 index 0000000000..96aeb3431a --- /dev/null +++ b/.changeset/fix-compaction-max-output-size.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix compaction ignoring the configured max output size. diff --git a/packages/agent-core/src/agent/compaction/full.ts b/packages/agent-core/src/agent/compaction/full.ts index 2d608bae5b..e5f134df0e 100644 --- a/packages/agent-core/src/agent/compaction/full.ts +++ b/packages/agent-core/src/agent/compaction/full.ts @@ -264,6 +264,7 @@ export class FullCompaction { const provider = applyCompletionBudget({ provider: this.agent.config.provider, budget: resolveCompletionBudget({ + maxOutputSize: this.agent.config.maxOutputSize, reservedContextSize: this.agent.kimiConfig?.loopControl?.reservedContextSize, }), capability: this.agent.config.modelCapabilities, diff --git a/packages/agent-core/test/agent/compaction/full.test.ts b/packages/agent-core/test/agent/compaction/full.test.ts index d905354929..4e392908aa 100644 --- a/packages/agent-core/test/agent/compaction/full.test.ts +++ b/packages/agent-core/test/agent/compaction/full.test.ts @@ -16,6 +16,7 @@ import { } from '@moonshot-ai/kosong'; import { afterEach, describe, expect, it, vi } from 'vitest'; +import type { KimiConfig } from '../../../src/config'; import type { AgentOptions } from '../../../src/agent'; import { DefaultCompactionStrategy, type CompactionStrategy } from '../../../src/agent/compaction'; import { FLAG_DEFINITIONS, MASTER_ENV } from '../../../src/flags'; @@ -1770,6 +1771,46 @@ describe('FullCompaction', () => { expect(compactionMaxCompletionTokens).toEqual([undefined]); }); + it('honors maxOutputSize from model config during compaction', async () => { + let callCount = 0; + const compactionMaxCompletionTokens: unknown[] = []; + const generate: GenerateFn = async (provider, _system, _tools, _history, callbacks) => { + callCount += 1; + if (callCount === 1) { + throw new APIContextOverflowError(400, 'Context length exceeded', 'req-max-output'); + } + if (callCount === 2) { + compactionMaxCompletionTokens.push(providerMaxCompletionTokens(provider)); + return textResult('Max output compacted summary.'); + } + await callbacks?.onMessagePart?.({ + type: 'text', + text: 'Recovered with max output.', + }); + return textResult('Recovered with max output.'); + }; + const ctx = testAgent({ generate }); + ctx.configure({ + provider: CATALOGUED_PROVIDER, + modelCapabilities: CATALOGUED_MODEL_CAPABILITIES, + }); + // Set maxOutputSize on the harness's internal kimiConfig — the + // compaction path reads it via ConfigState.maxOutputSize. + const models = (ctx as unknown as { kimiConfig: KimiConfig }).kimiConfig.models; + models![CATALOGUED_PROVIDER.model] = { + ...models![CATALOGUED_PROVIDER.model]!, + maxOutputSize: 384000, + }; + ctx.appendExchange(1, 'old user one', 'old assistant one', 20); + ctx.newEvents(); + + await ctx.rpc.prompt({ input: [{ type: 'text', text: 'Retry with max output' }] }); + await ctx.untilTurnEnd(); + + expect(callCount).toBe(3); + expect(compactionMaxCompletionTokens).toEqual([384000]); + }); + it('ignores filtered assistant placeholders when checking the retained overflow suffix', async () => { let callCount = 0; const generate: GenerateFn = async (_provider, _system, _tools, _history, callbacks) => {