From cea5f98c296e2a8f9a791b3d546f2ccd321ee2e1 Mon Sep 17 00:00:00 2001 From: Vitalii Yehorov Date: Sun, 4 Oct 2026 06:23:43 +0200 Subject: [PATCH] fix(claude): a thread with background work no longer refuses messages sent from another client Mobile omits fastMode while web sends fastMode: false. The compiled query identity told them apart, so a Claude process opened from one client looked reconfigured by the other, and the background-work guard refused every message until the work ended. Unset now compiles as Normal, the meaning web already sends. Co-Authored-By: Claude Opus 5.5 (1M context) --- apps/server/src/claudeModelOptions.test.ts | 9 +++++++++ apps/server/src/claudeModelOptions.ts | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/server/src/claudeModelOptions.test.ts b/apps/server/src/claudeModelOptions.test.ts index 005609008f33..ee5718eb87f2 100644 --- a/apps/server/src/claudeModelOptions.test.ts +++ b/apps/server/src/claudeModelOptions.test.ts @@ -40,6 +40,15 @@ describe("compileClaudeModelSelection", () => { ).toEqual({ fastMode: false }); }); + it("treats unset fast mode as Normal so clients that omit it reuse the same query", () => { + const unset = compileClaudeModelSelection(selection("claude-opus-4-6", [])); + const normal = compileClaudeModelSelection( + selection("claude-opus-4-6", [{ id: "fastMode", value: false }]), + ); + expect(unset.settings).toEqual({ fastMode: false }); + expect(unset.queryIdentity).toBe(normal.queryIdentity); + }); + it("uses the model default SDK effort alongside prompt-injected effort", () => { expect( compileClaudeModelSelection( diff --git a/apps/server/src/claudeModelOptions.ts b/apps/server/src/claudeModelOptions.ts index d018c2f0af32..e11f4e326419 100644 --- a/apps/server/src/claudeModelOptions.ts +++ b/apps/server/src/claudeModelOptions.ts @@ -36,8 +36,10 @@ export function compileClaudeModelSelection( const rawEffort = getModelSelectionStringOptionValue(selection, "effort"); const resolvedEffort = resolveClaudeCatalogEffort(catalog, selection.model, rawEffort); const effort = normalizeClaudeCatalogEffort(catalog, resolvedEffort, selection.model); + // Unset means Normal, as the web composer sends it. Keeping the two apart + // would make a live process look reconfigured when only the client changed. const fastMode = supportsBoolean("fastMode") - ? getModelSelectionBooleanOptionValue(selection, "fastMode") + ? (getModelSelectionBooleanOptionValue(selection, "fastMode") ?? false) : undefined; const thinking = supportsBoolean("thinking") ? getModelSelectionBooleanOptionValue(selection, "thinking")