- Snapshot sessions Map with Array.from() in stopAll and finalizer to prevent entries being skipped when stopSessionInternal deletes during iteration (ClaudeCodeAdapter.ts) - Stop resolving aliases in normalizeCustomModelSlugs so dated model versions like claude-sonnet-4-6-20251117 are not silently dropped as built-in duplicates (appSettings.ts) - Remove incorrect model: gpt-5-codex assertion from claudeCode provider test since the test validates provider selection, not model resolution (ProviderCommandReactor.test.ts)
Unified · Split
| @@ -416,7 +416,6 @@ describe("ProviderCommandReactor", () => { · 415 unmodified lines | |||
| 416 | expect(harness.startSession.mock.calls[0]?.[1]).toMatchObject({ | 416 | expect(harness.startSession.mock.calls[0]?.[1]).toMatchObject({ |
| 417 | provider: "claudeCode", | 417 | provider: "claudeCode", |
| 418 | cwd: "/tmp/provider-project", | 418 | cwd: "/tmp/provider-project", |
| 419 | model: "gpt-5-codex", | ||
| 420 | runtimeMode: "approval-required", | 419 | runtimeMode: "approval-required", |
| 421 | }); | 420 | }); |
| 422 | 421 | ||
| @@ -1816,7 +1816,7 @@ function makeClaudeCodeAdapter(options?: ClaudeCodeAdapterLiveOptions) { · 1815 unmodified lines | |||
| 1816 | 1816 | ||
| 1817 | const stopAll: ClaudeCodeAdapterShape["stopAll"] = () => | 1817 | const stopAll: ClaudeCodeAdapterShape["stopAll"] = () => |
| 1818 | Effect.forEach( | 1818 | Effect.forEach( |
| 1819 | sessions, | 1819 | Array.from(sessions), |
| 1820 | ([, context]) => | 1820 | ([, context]) => |
| 1821 | stopSessionInternal(context, { | 1821 | stopSessionInternal(context, { |
| 1822 | emitExitEvent: true, | 1822 | emitExitEvent: true, |
| @@ -1826,7 +1826,7 @@ function makeClaudeCodeAdapter(options?: ClaudeCodeAdapterLiveOptions) { · 3 unmodified lines | |||
| 1826 | 1826 | ||
| 1827 | yield* Effect.addFinalizer(() => | 1827 | yield* Effect.addFinalizer(() => |
| 1828 | Effect.forEach( | 1828 | Effect.forEach( |
| 1829 | sessions, | 1829 | Array.from(sessions), |
| 1830 | ([, context]) => | 1830 | ([, context]) => |
| 1831 | stopSessionInternal(context, { | 1831 | stopSessionInternal(context, { |
| 1832 | emitExitEvent: false, | 1832 | emitExitEvent: false, |
| @@ -8,12 +8,11 @@ import { · 7 unmodified lines | |||
| 8 | } from "./appSettings"; | 8 | } from "./appSettings"; |
| 9 | 9 | ||
| 10 | describe("normalizeCustomModelSlugs", () => { | 10 | describe("normalizeCustomModelSlugs", () => { |
| 11 | it("normalizes aliases, removes built-ins, and deduplicates values", () => { | 11 | it("removes literal built-ins, trims whitespace, and deduplicates values", () => { |
| 12 | expect( | 12 | expect( |
| 13 | normalizeCustomModelSlugs([ | 13 | normalizeCustomModelSlugs([ |
| 14 | " custom/internal-model ", | 14 | " custom/internal-model ", |
| 15 | "gpt-5.3-codex", | 15 | "gpt-5.3-codex", |
| 16 | "5.3", | ||
| 17 | "custom/internal-model", | 16 | "custom/internal-model", |
| 18 | "", | 17 | "", |
| 19 | null, | 18 | null, |
| @@ -21,8 +20,10 @@ describe("normalizeCustomModelSlugs", () => { · 1 unmodified line | |||
| 21 | ).toEqual(["custom/internal-model"]); | 20 | ).toEqual(["custom/internal-model"]); |
| 22 | }); | 21 | }); |
| 23 | 22 | ||
| 24 | it("normalizes provider-specific aliases for claude", () => { | 23 | it("preserves aliases and dated versions as custom models", () => { |
| 25 | expect(normalizeCustomModelSlugs(["sonnet"], "claudeCode")).toEqual([]); | 24 | expect(normalizeCustomModelSlugs(["claude-sonnet-4-6-20251117"], "claudeCode")).toEqual([ |
| 25 | "claude-sonnet-4-6-20251117", | ||
| 26 | ]); | ||
| 26 | expect(normalizeCustomModelSlugs(["claude/custom-sonnet"], "claudeCode")).toEqual([ | 27 | expect(normalizeCustomModelSlugs(["claude/custom-sonnet"], "claudeCode")).toEqual([ |
| 27 | "claude/custom-sonnet", | 28 | "claude/custom-sonnet", |
| 28 | ]); | 29 | ]); |
| @@ -57,18 +57,19 @@ export function normalizeCustomModelSlugs( · 56 unmodified lines | |||
| 57 | const builtInModelSlugs = BUILT_IN_MODEL_SLUGS_BY_PROVIDER[provider]; | 57 | const builtInModelSlugs = BUILT_IN_MODEL_SLUGS_BY_PROVIDER[provider]; |
| 58 | 58 | ||
| 59 | for (const candidate of models) { | 59 | for (const candidate of models) { |
| 60 | const normalized = normalizeModelSlug(candidate, provider); | 60 | if (typeof candidate !== "string") continue; |
| 61 | const trimmed = candidate.trim(); | ||
| 61 | if ( | 62 | if ( |
| 62 | !normalized || | 63 | !trimmed || |
| 63 | normalized.length > MAX_CUSTOM_MODEL_LENGTH || | 64 | trimmed.length > MAX_CUSTOM_MODEL_LENGTH || |
| 64 | builtInModelSlugs.has(normalized) || | 65 | builtInModelSlugs.has(trimmed) || |
| 65 | seen.has(normalized) | 66 | seen.has(trimmed) |
| 66 | ) { | 67 | ) { |
| 67 | continue; | 68 | continue; |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | seen.add(normalized); | 71 | seen.add(trimmed); |
| 71 | normalizedModels.push(normalized); | 72 | normalizedModels.push(trimmed); |
| 72 | if (normalizedModels.length >= MAX_CUSTOM_MODEL_COUNT) { | 73 | if (normalizedModels.length >= MAX_CUSTOM_MODEL_COUNT) { |
| 73 | break; | 74 | break; |
| 74 | } | 75 | } |