fix: align manual native compaction fallback

(cherry picked from commit 2d5397a52f6feaeee4136fe4e7d8271309043b75)
This commit is contained in:
usr-bin-roygbiv
2026-07-28 15:46:12 +00:00
committed by can1357
parent 9dd8d3c6ce
commit 64234e05c9
2 changed files with 44 additions and 2 deletions
@@ -1460,10 +1460,18 @@ export class SessionMaintenance {
const candidates =
precomputedCandidates ?? this.#getCompactionModelCandidates(this.#host.modelRegistry.getAvailable());
const telemetry = resolveTelemetry(this.#host.agent.telemetry, this.#host.sessionId());
let nativeCompactionFailure: { error: NativeCompactionError; provider: string } | undefined;
for (const candidate of candidates) {
const apiKey = await this.#host.modelRegistry.getApiKey(candidate, this.#host.sessionId());
if (!apiKey) continue;
if (
nativeCompactionFailure &&
(candidate.provider !== nativeCompactionFailure.provider ||
!shouldUseProviderNativeCompaction(candidate, preparation.settings))
) {
throw nativeCompactionFailure.error;
}
try {
return await compact(
@@ -1501,12 +1509,17 @@ export class SessionMaintenance {
},
);
} catch (error) {
if (!AIError.is(AIError.classify(error, candidate.api), AIError.Flag.AuthFailed)) {
throw error;
const id = AIError.classify(error instanceof NativeCompactionError ? error.cause : error, candidate.api);
if (AIError.is(id, AIError.Flag.AuthFailed)) continue;
if (error instanceof NativeCompactionError) {
nativeCompactionFailure ??= { error, provider: candidate.provider };
continue;
}
throw error;
}
}
if (nativeCompactionFailure) throw nativeCompactionFailure.error;
throw this.#buildCompactionAuthError();
}
@@ -373,6 +373,35 @@ describe("issue #986 compaction auth fallback", () => {
]);
});
it("tries same-provider native candidates during manual compaction before crossing providers", async () => {
const { crossProviderModel, currentModel, sameProviderModel } = await createAutoNativeFallbackSession();
const attemptedModels: string[] = [];
vi.spyOn(compactionModule, "compact").mockImplementation(async (preparation, model) => {
attemptedModels.push(`${model.provider}/${model.id}`);
if (model.provider === currentModel.provider && model.id === currentModel.id) {
throw new compactionModule.NativeCompactionError(new Error("native manual compaction failed"));
}
if (model.provider === sameProviderModel.provider && model.id === sameProviderModel.id) {
return {
summary: "same-provider manual summary",
shortSummary: "same-provider manual",
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: 42,
};
}
throw new Error(`Unexpected compaction model ${model.provider}/${model.id}`);
});
const result = await session.compact();
expect(result.summary).toBe("same-provider manual summary");
expect(attemptedModels).toEqual([
`${currentModel.provider}/${currentModel.id}`,
`${sameProviderModel.provider}/${sameProviderModel.id}`,
]);
expect(attemptedModels).not.toContain(`${crossProviderModel.provider}/${crossProviderModel.id}`);
});
it("falls back across providers when native compaction receives auth_unavailable", async () => {
const { currentModel, fallbackModel } = await createSession({ fallbackModelRole: "smol" });
const originalCompact = compactionModule.compact;