From 5d9cbf19e62f513781a461c03a7c385e6bc25e59 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 28 Jul 2026 08:06:17 +0000 Subject: [PATCH] fix(session): preserved auto-thinking level on classifier failure - Kept the last successfully classified effort when a later classification fails. - Added regression coverage for the success-then-failure transition. Fixes #6877 --- packages/coding-agent/CHANGELOG.md | 4 +++ .../src/session/model-controls.ts | 8 +++--- .../test/agent-session-role-thinking.test.ts | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 8379caed6..8339791ff 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Preserved the active auto-thinking effort when per-turn classification fails, avoiding an unnecessary full prompt-cache invalidation from reverting to the model's provisional default ([#6877](https://github.com/can1357/oh-my-pi/issues/6877)). + ## [17.1.7] - 2026-07-27 ### Fixed diff --git a/packages/coding-agent/src/session/model-controls.ts b/packages/coding-agent/src/session/model-controls.ts index 364e76f67..4f195d03c 100644 --- a/packages/coding-agent/src/session/model-controls.ts +++ b/packages/coding-agent/src/session/model-controls.ts @@ -581,9 +581,9 @@ export class ModelControls { /** * Classify the current user turn and set the effective thinking level for it. - * Bounded by a timeout + abort; on any failure (no smol model, timeout, parse - * error) it falls back to the provisional concrete level and continues. Never - * throws into the turn, and never clears `#autoThinking` (auto stays active). + * Bounded by a timeout + abort; on failure it preserves the last classified + * level, or uses the provisional concrete level before the first resolution. + * Never throws into the turn, and never clears `#autoThinking`. */ async applyAutoThinkingLevel(promptText: string, generation: number): Promise { const model = this.#model; @@ -625,7 +625,7 @@ export class ModelControls { const effort = clampThinkingLevelToCeiling( model, - resolved ?? resolveProvisionalAutoLevel(model), + resolved ?? this.#autoResolvedLevel ?? resolveProvisionalAutoLevel(model), this.#thinkingLevelCeiling, ); if (effort === undefined) return; diff --git a/packages/coding-agent/test/agent-session-role-thinking.test.ts b/packages/coding-agent/test/agent-session-role-thinking.test.ts index 311155359..481f25093 100644 --- a/packages/coding-agent/test/agent-session-role-thinking.test.ts +++ b/packages/coding-agent/test/agent-session-role-thinking.test.ts @@ -541,6 +541,34 @@ describe("AgentSession role model thinking behavior", () => { ); }); + it("preserves the resolved auto level when a later classification fails", async () => { + const model = getAnthropicModelOrThrow("claude-sonnet-4-5"); + await createSession({ + initialModelId: model.id, + initialThinkingLevel: Effort.High, + modelRoles: { default: `${model.provider}/${model.id}` }, + }); + vi.spyOn(session.agent, "prompt").mockResolvedValue(undefined); + vi.spyOn(autoThinkingClassifier, "classifyDifficulty") + .mockResolvedValueOnce(Effort.Low) + .mockRejectedValueOnce(new Error("classifier down")); + + session.setThinkingLevel(AUTO_THINKING); + await session.prompt("Handle a straightforward update"); + const receiptCount = session.sessionManager + .getEntries() + .filter(entry => entry.type === "thinking_level_change").length; + await session.prompt("Investigate another update"); + + expect(session.configuredThinkingLevel()).toBe(AUTO_THINKING); + expect(session.thinkingLevel).toBe(Effort.Low); + expect(session.autoResolvedThinkingLevel()).toBe(Effort.Low); + expect(session.agent.state.thinkingLevel).toBe(Effort.Low); + expect(session.sessionManager.getEntries().filter(entry => entry.type === "thinking_level_change")).toHaveLength( + receiptCount, + ); + }); + it("skips classification for synthetic turns", async () => { const model = getAnthropicModelOrThrow("claude-sonnet-4-5"); await createSession({