From e0dcd94c0eec9728201e12bf78ddecec247df438 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sat, 31 Oct 2026 06:57:37 +0000 Subject: [PATCH 1/2] test(server): reproduce #28008 stale Cursor models after a completed refresh These tests reproduce #28008: a ready Cursor probe should replace the model list, while a failed probe keeps the previous models. Co-Authored-By: Claude Opus 5.5 --- .../src/provider/ProviderRegistry.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/apps/server/src/provider/ProviderRegistry.test.ts b/apps/server/src/provider/ProviderRegistry.test.ts index 60aad9c59977a..0cf60e51e7fde 100644 --- a/apps/server/src/provider/ProviderRegistry.test.ts +++ b/apps/server/src/provider/ProviderRegistry.test.ts @@ -891,6 +891,45 @@ it.layer( ); }); + it("drops Cursor models a completed catalog read no longer returns", () => { + const previousProvider = { + instanceId: ProviderInstanceId.make("cursor"), + driver: ProviderDriverKind.make("cursor"), + status: "ready", + enabled: true, + installed: true, + auth: { status: "authenticated" }, + checkedAt: "2026-10-30T00:00:00.000Z", + version: "1.0.0", + models: [ + { slug: "composer-2", name: "Composer 2", isCustom: false, capabilities: null }, + { slug: "retired-model-x", name: "Retired", isCustom: false, capabilities: null }, + ], + slashCommands: [], + skills: [], + } as const satisfies ServerProvider; + const refreshedProvider = { + ...previousProvider, + checkedAt: "2026-10-30T00:01:00.000Z", + models: [previousProvider.models[0]], + } satisfies ServerProvider; + const failedProvider = { + ...refreshedProvider, + status: "error", + auth: { status: "unknown" }, + message: "Cursor SDK model discovery timed out.", + } satisfies ServerProvider; + + assert.deepStrictEqual( + ProviderRegistry.mergeProviderSnapshot(previousProvider, refreshedProvider).models, + [previousProvider.models[0]], + ); + assert.deepStrictEqual( + ProviderRegistry.mergeProviderSnapshot(previousProvider, failedProvider).models, + [...previousProvider.models], + ); + }); + it("retains stale OpenCode models when a refresh fails", () => { const previousProvider = { instanceId: ProviderInstanceId.make("opencode"), From 8b2004be5f4f925682fee63160374201fc349a76 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sat, 31 Oct 2026 07:01:34 +0000 Subject: [PATCH 2/2] fix(cursor): a completed catalog refresh drops models it no longer lists Cursor fell through to the default "retain missing models" rule, so a refresh could only add models and a previous account's or retired plan's models stayed in the snapshot and provider cache forever. Cursor reports ready only after a non-empty SDK catalog read, the same contract as Grok, so it now shares Grok's rule: a ready probe replaces the list, while warning/error probes keep the previous models. Two generic retention tests used Cursor with a ready, empty refresh, a state the Cursor probe never produces; they now use the warning status an empty catalog actually reports. Fixes pingdotgg/t3code#28008 Co-Authored-By: Claude Opus 5.5 --- apps/server/src/provider/ProviderRegistry.test.ts | 2 ++ apps/server/src/provider/ProviderRegistry.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/server/src/provider/ProviderRegistry.test.ts b/apps/server/src/provider/ProviderRegistry.test.ts index 0cf60e51e7fde..225af48e7c9ba 100644 --- a/apps/server/src/provider/ProviderRegistry.test.ts +++ b/apps/server/src/provider/ProviderRegistry.test.ts @@ -746,6 +746,7 @@ it.layer( } as const satisfies ServerProvider; const refreshedProvider = { ...previousProvider, + status: "warning", checkedAt: "2026-04-14T00:01:00.000Z", models: [], slashCommands: [], @@ -1836,6 +1837,7 @@ it.layer( } as const satisfies ServerProvider; const refreshedProvider = { ...initialProvider, + status: "warning", checkedAt: "2026-04-14T00:01:00.000Z", models: [], } satisfies ServerProvider; diff --git a/apps/server/src/provider/ProviderRegistry.ts b/apps/server/src/provider/ProviderRegistry.ts index 3234bfd61960a..a73641d3bf57c 100644 --- a/apps/server/src/provider/ProviderRegistry.ts +++ b/apps/server/src/provider/ProviderRegistry.ts @@ -176,9 +176,12 @@ const shouldRetainMissingProviderModels = (provider: ServerProvider): boolean => ); } - if (provider.driver === ProviderDriverKind.make("grok")) { - // Grok reports ready only after it discovers a model list; the pending, - // ACP-failed, empty-inventory, and logged-out probes are warning/error. + if ( + provider.driver === ProviderDriverKind.make("grok") || + provider.driver === ProviderDriverKind.make("cursor") + ) { + // Grok and Cursor report ready only after they discover a model list; the pending, + // failed, empty-inventory, and logged-out probes are warning/error. return !(provider.installed && provider.status === "ready"); }