mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-13 18:56:12 +00:00
fix: key harness resume coalescing by the full input
Concurrent resumes only share a facade when their inputs match — a caller passing different dirs, replay, profile, or kaos options gets its own resume instead of having its options silently dropped.
This commit is contained in:
parent
cd1cea0fd5
commit
7292ccc018
2 changed files with 42 additions and 7 deletions
|
|
@ -153,18 +153,21 @@ export class KimiHarness {
|
|||
return active;
|
||||
}
|
||||
|
||||
// Coalesce concurrent resumes of the same id onto one facade; without
|
||||
// this, parallel callers each build their own Session over the shared
|
||||
// engine handle, and one facade's close kills the engine handle under
|
||||
// the other.
|
||||
const inflight = this.resumeInflight.get(id);
|
||||
// Coalesce concurrent resumes of the same id onto one facade, keyed by
|
||||
// the full input so a caller with different options (dirs, replay,
|
||||
// profile, kaos) never has them silently dropped; without this,
|
||||
// parallel identical callers each build their own Session over the
|
||||
// shared engine handle, and one facade's close kills the engine handle
|
||||
// under the other.
|
||||
const key = resumeCoalesceKey(id, input);
|
||||
const inflight = this.resumeInflight.get(key);
|
||||
if (inflight !== undefined) return inflight;
|
||||
const run = this.doResumeSession(input, id);
|
||||
this.resumeInflight.set(id, run);
|
||||
this.resumeInflight.set(key, run);
|
||||
try {
|
||||
return await run;
|
||||
} finally {
|
||||
if (this.resumeInflight.get(id) === run) this.resumeInflight.delete(id);
|
||||
if (this.resumeInflight.get(key) === run) this.resumeInflight.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -404,6 +407,16 @@ export class KimiHarness {
|
|||
|
||||
const DEFAULT_SESSION_STARTED_UI_MODE = 'shell';
|
||||
|
||||
function resumeCoalesceKey(id: string, input: ResumeSessionInput): string {
|
||||
const { kaos, persistenceKaos, ...rest } = input;
|
||||
return JSON.stringify({
|
||||
...rest,
|
||||
id,
|
||||
kaos: kaos !== undefined,
|
||||
persistenceKaos: persistenceKaos !== undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeSessionId(value: string): string {
|
||||
if (typeof value !== 'string') {
|
||||
throw new KimiError(ErrorCodes.SESSION_ID_REQUIRED, 'Session id is required.');
|
||||
|
|
|
|||
|
|
@ -395,6 +395,28 @@ key = "${titleOAuthRef.key}"
|
|||
}
|
||||
});
|
||||
|
||||
it('does not coalesce resumes with different options onto one facade', async () => {
|
||||
const { harness } = await makeHarness();
|
||||
const workDir = await mkdtemp(join(tmpdir(), 'kimi-sdk-v2-work-'));
|
||||
tempDirs.push(workDir);
|
||||
|
||||
try {
|
||||
const session = await harness.createSession({ id: 'ses_no_coalesce', workDir });
|
||||
await session.close();
|
||||
|
||||
const [plain, withReplay] = await Promise.all([
|
||||
harness.resumeSession({ id: 'ses_no_coalesce' }),
|
||||
harness.resumeSession({ id: 'ses_no_coalesce', replayTurnLimit: 3 }),
|
||||
]);
|
||||
|
||||
// Different options must not be silently dropped onto the first
|
||||
// caller's facade — each gets its own resume.
|
||||
expect(plain).not.toBe(withReplay);
|
||||
} finally {
|
||||
await harness.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('reports the title state in listSessions as well as in the resumed summary', async () => {
|
||||
const { harness } = await makeHarness();
|
||||
const workDir = await mkdtemp(join(tmpdir(), 'kimi-sdk-v2-work-'));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue