mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
fix: stop recording resume version metadata (#786)
This commit is contained in:
parent
73be7ba17d
commit
e10b25f9be
8 changed files with 59 additions and 22 deletions
6
.changeset/clean-resume-metadata.md
Normal file
6
.changeset/clean-resume-metadata.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
"@moonshot-ai/agent-core": patch
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Stop writing resume version markers into persisted agent metadata.
|
||||
|
|
@ -86,7 +86,6 @@ export interface AgentOptions {
|
|||
readonly log?: Logger;
|
||||
readonly telemetry?: TelemetryClient | undefined;
|
||||
readonly pluginSessionStarts?: readonly EnabledPluginSessionStart[];
|
||||
readonly appVersion?: string;
|
||||
readonly experimentalFlags?: ExperimentalFlagResolver;
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +109,6 @@ export class Agent {
|
|||
readonly hooks?: HookEngine;
|
||||
readonly log: Logger;
|
||||
readonly telemetry: TelemetryClient;
|
||||
readonly appVersion?: string;
|
||||
readonly experimentalFlags: ExperimentalFlagResolver;
|
||||
|
||||
readonly blobStore: BlobStore | undefined;
|
||||
|
|
@ -147,7 +145,6 @@ export class Agent {
|
|||
this.subagentHost = options.subagentHost;
|
||||
this.mcp = options.mcp;
|
||||
this.hooks = options.hookEngine;
|
||||
this.appVersion = options.appVersion;
|
||||
this.log = options.log ?? log;
|
||||
this.telemetry = options.telemetry ?? noopTelemetryClient;
|
||||
this.experimentalFlags = options.experimentalFlags ?? new FlagResolver();
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ export class AgentRecords {
|
|||
type: 'metadata',
|
||||
protocol_version: AGENT_WIRE_PROTOCOL_VERSION,
|
||||
created_at: Date.now(),
|
||||
app_version: this.agent.appVersion,
|
||||
});
|
||||
this.metadataInitialized = true;
|
||||
}
|
||||
|
|
@ -217,20 +216,6 @@ export class AgentRecords {
|
|||
await this.agent.blobStore.rehydrateParts(msg.content);
|
||||
}
|
||||
}
|
||||
const firstRecord = replayedRecords[0];
|
||||
if (
|
||||
firstRecord?.type === 'metadata' &&
|
||||
firstRecord.app_version !== this.agent.appVersion
|
||||
) {
|
||||
this.persistence.append({
|
||||
type: 'metadata',
|
||||
protocol_version: AGENT_WIRE_PROTOCOL_VERSION,
|
||||
created_at: Date.now(),
|
||||
app_version: this.agent.appVersion,
|
||||
resumed: true,
|
||||
});
|
||||
await this.persistence.flush();
|
||||
}
|
||||
return { warning };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ export interface AgentRecordEvents {
|
|||
metadata: {
|
||||
protocol_version: string;
|
||||
created_at: number;
|
||||
app_version?: string;
|
||||
resumed?: boolean;
|
||||
};
|
||||
|
||||
forked: {};
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ export class Session {
|
|||
|
||||
async resume(): Promise<{ warning?: string }> {
|
||||
await this.skillsReady;
|
||||
this.log.info('session resume', { app_version: this.options.appVersion });
|
||||
const { agents } = await this.readMetadata();
|
||||
this.agents.clear();
|
||||
// Only the main agent is needed to reopen the session; subagents replay
|
||||
|
|
@ -573,7 +574,6 @@ export class Session {
|
|||
telemetry: this.telemetry,
|
||||
log: this.log.createChild({ agentId: id }),
|
||||
pluginSessionStarts: type === 'main' ? this.options.pluginSessionStarts : undefined,
|
||||
appVersion: this.options.appVersion,
|
||||
experimentalFlags: this.experimentalFlags,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ describe('AgentRecords persistence metadata', () => {
|
|||
type: 'metadata',
|
||||
protocol_version: AGENT_WIRE_PROTOCOL_VERSION,
|
||||
});
|
||||
expect(persistence.records[0]).not.toHaveProperty('app_version');
|
||||
expect(persistence.records[0]).not.toHaveProperty('resumed');
|
||||
expect(persistence.records[1]?.type).toBe('turn.prompt');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,28 @@ const MOCK_PROVIDER = {
|
|||
} as const;
|
||||
|
||||
describe('Agent resume', () => {
|
||||
it('does not append metadata when resuming records that include legacy app version', async () => {
|
||||
const persistence = new RecordingAgentPersistence([
|
||||
{
|
||||
type: 'metadata',
|
||||
protocol_version: AGENT_WIRE_PROTOCOL_VERSION,
|
||||
created_at: 1,
|
||||
app_version: '0.0.1-old',
|
||||
} as unknown as AgentRecord,
|
||||
{
|
||||
type: 'turn.prompt',
|
||||
input: [{ type: 'text', text: 'old prompt' }],
|
||||
origin: { kind: 'user' },
|
||||
},
|
||||
]);
|
||||
const ctx = testAgent({ persistence });
|
||||
|
||||
await ctx.agent.resume();
|
||||
|
||||
expect(persistence.appended).toEqual([]);
|
||||
expect(persistence.records.filter((record) => record.type === 'metadata')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('replays persisted records without restarting turns, compactions, plan turns, or tools', async () => {
|
||||
const persistence = new RecordingAgentPersistence(resumeHistory());
|
||||
const execWithEnv = vi.fn().mockRejectedValue(new Error('Bash should not execute on resume'));
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ import {
|
|||
type SDKAPI,
|
||||
type TelemetryClient,
|
||||
} from '../../src';
|
||||
import {
|
||||
__resetRootLoggerForTest,
|
||||
getRootLogger,
|
||||
} from '../../src/logging/logger';
|
||||
import { resolveLoggingConfig } from '../../src/logging/resolve-config';
|
||||
import {
|
||||
recordingContextTelemetry,
|
||||
type TelemetryContextRecord,
|
||||
|
|
@ -46,6 +51,7 @@ describe('HarnessAPI session model aliases', () => {
|
|||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await __resetRootLoggerForTest();
|
||||
await rm(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
|
|
@ -277,6 +283,21 @@ reason = "no rm"
|
|||
);
|
||||
});
|
||||
|
||||
it('logs app_version when resuming a session', async () => {
|
||||
await getRootLogger().configure(resolveLoggingConfig({ homeDir }));
|
||||
const rpc = await createTestRpc();
|
||||
const created = await rpc.createSession({ workDir, model: 'kimi-code/kimi-for-coding' });
|
||||
await rpc.closeSession({ sessionId: created.id });
|
||||
|
||||
const freshRpc = await createTestRpc({ appVersion: '1.2.3-test' });
|
||||
await freshRpc.resumeSession({ sessionId: created.id });
|
||||
await getRootLogger().flushSession(created.id);
|
||||
|
||||
const logText = await readFile(join(created.sessionDir, 'logs', 'kimi-code.log'), 'utf-8');
|
||||
expect(logText).toContain('session resume');
|
||||
expect(logText).toContain('app_version=1.2.3-test');
|
||||
});
|
||||
|
||||
it('surfaces a config error when a resumed model is configured but unresolvable', async () => {
|
||||
const rpc = await createTestRpc();
|
||||
const created = await rpc.createSession({ workDir, model: 'kimi-code/kimi-for-coding' });
|
||||
|
|
@ -397,11 +418,17 @@ max_context_size = 1000000
|
|||
return join(root, match);
|
||||
}
|
||||
|
||||
async function createTestRpc(options: { readonly telemetry?: TelemetryClient } = {}) {
|
||||
async function createTestRpc(
|
||||
options: {
|
||||
readonly appVersion?: string;
|
||||
readonly telemetry?: TelemetryClient;
|
||||
} = {},
|
||||
) {
|
||||
const [coreRpc, sdkRpc] = createRPC<CoreAPI, SDKAPI>();
|
||||
void new KimiCore(coreRpc, {
|
||||
homeDir,
|
||||
configPath,
|
||||
appVersion: options.appVersion,
|
||||
telemetry: options.telemetry,
|
||||
});
|
||||
return sdkRpc({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue