| 1 | import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts"; |
| 2 | import { describe, expect, it } from "vite-plus/test"; |
| 3 | import { |
| 4 | applyProviderInstanceSettings, |
| 5 | deriveProviderEntriesByEnvironment, |
| 6 | deriveProviderInstanceEntries, |
| 7 | getDefaultProviderInstanceModel, |
| 8 | isProviderInstancePickerReady, |
| 9 | isProviderInstancePickerVisible, |
| 10 | resolveDefaultProviderModelSelection, |
| 11 | resolveProviderCatalogAvailability, |
| 12 | resolveSelectableProviderInstance, |
| 13 | } from "./providerInstances"; |
| 14 | |
| 15 | function provider(input: { |
| 16 | provider: ProviderDriverKind; |
| 17 | instanceId: string; |
| 18 | enabled?: boolean; |
| 19 | availability?: ServerProvider["availability"]; |
| 20 | displayName?: string; |
| 21 | accentColor?: string; |
| 22 | status?: ServerProvider["status"]; |
| 23 | models?: ServerProvider["models"]; |
| 24 | supportsTextGeneration?: boolean; |
| 25 | }): ServerProvider { |
| 26 | return { |
| 27 | instanceId: ProviderInstanceId.make(input.instanceId), |
| 28 | driver: input.provider, |
| 29 | ...(input.supportsTextGeneration === undefined |
| 30 | ? {} |
| 31 | : { supportsTextGeneration: input.supportsTextGeneration }), |
| 32 | ...(input.displayName ? { displayName: input.displayName } : {}), |
| 33 | ...(input.accentColor ? { accentColor: input.accentColor } : {}), |
| 34 | enabled: input.enabled ?? true, |
| 35 | installed: true, |
| 36 | version: null, |
| 37 | status: input.status ?? "ready", |
| 38 | ...(input.availability ? { availability: input.availability } : {}), |
| 39 | auth: { status: "authenticated" }, |
| 40 | checkedAt: "2026-01-01T00:00:00.000Z", |
| 41 | models: input.models ?? [], |
| 42 | slashCommands: [], |
| 43 | skills: [], |
| 44 | }; |
| 45 | } |
| 46 | |
| 47 | const model = (slug: string, isCustom = false, isDefault = false) => ({ |
| 48 | slug, |
| 49 | name: slug, |
| 50 | isCustom, |
| 51 | ...(isDefault ? { isDefault: true } : {}), |
| 52 | capabilities: {}, |
| 53 | }); |
| 54 | |
| 55 | describe("isProviderInstancePickerReady", () => { |
| 56 | it("rejects a disabled instance even while its last probe status is ready", () => { |
| 57 | const [entry] = deriveProviderInstanceEntries([ |
| 58 | provider({ |
| 59 | provider: ProviderDriverKind.make("codex"), |
| 60 | instanceId: "codex", |
| 61 | enabled: false, |
| 62 | }), |
| 63 | ]); |
| 64 | |
| 65 | expect(entry?.status).toBe("ready"); |
| 66 | expect(entry && isProviderInstancePickerReady(entry)).toBe(false); |
| 67 | }); |
| 68 | |
| 69 | it("accepts an enabled, available, ready instance", () => { |
| 70 | const [entry] = deriveProviderInstanceEntries([ |
| 71 | provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex" }), |
| 72 | ]); |
| 73 | |
| 74 | expect(entry && isProviderInstancePickerReady(entry)).toBe(true); |
| 75 | }); |
| 76 | }); |
| 77 | |
| 78 | describe("resolveProviderCatalogAvailability", () => { |
| 79 | const [entry] = deriveProviderInstanceEntries([ |
| 80 | provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex" }), |
| 81 | ]); |
| 82 | |
| 83 | it("keeps the initial reactive config state distinct from an empty catalogue", () => { |
| 84 | expect( |
| 85 | resolveProviderCatalogAvailability({ |
| 86 | catalogLoaded: false, |
| 87 | entries: [], |
| 88 | selectedEntry: undefined, |
| 89 | }), |
| 90 | ).toBe("loading"); |
| 91 | expect( |
| 92 | resolveProviderCatalogAvailability({ |
| 93 | catalogLoaded: true, |
| 94 | entries: [], |
| 95 | selectedEntry: undefined, |
| 96 | }), |
| 97 | ).toBe("unconfigured"); |
| 98 | }); |
| 99 | |
| 100 | it("distinguishes a selected provider from configured but unusable providers", () => { |
| 101 | expect( |
| 102 | resolveProviderCatalogAvailability({ |
| 103 | catalogLoaded: true, |
| 104 | entries: entry ? [entry] : [], |
| 105 | selectedEntry: entry, |
| 106 | }), |
| 107 | ).toBe("ready"); |
| 108 | expect( |
| 109 | resolveProviderCatalogAvailability({ |
| 110 | catalogLoaded: true, |
| 111 | entries: entry ? [entry] : [], |
| 112 | selectedEntry: undefined, |
| 113 | }), |
| 114 | ).toBe("unavailable"); |
| 115 | }); |
| 116 | }); |
| 117 | |
| 118 | describe("isProviderInstancePickerVisible", () => { |
| 119 | it("keeps enabled instances in the rail and removes disabled instances", () => { |
| 120 | const [enabledEntry, disabledEntry] = deriveProviderInstanceEntries([ |
| 121 | provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex" }), |
| 122 | provider({ |
| 123 | provider: ProviderDriverKind.make("claudeAgent"), |
| 124 | instanceId: "claudeAgent", |
| 125 | enabled: false, |
| 126 | }), |
| 127 | ]); |
| 128 | |
| 129 | expect(enabledEntry && isProviderInstancePickerVisible(enabledEntry)).toBe(true); |
| 130 | expect(disabledEntry && isProviderInstancePickerVisible(disabledEntry)).toBe(false); |
| 131 | }); |
| 132 | }); |
| 133 | |
| 134 | describe("applyProviderInstanceSettings", () => { |
| 135 | it("uses settings when a streamed snapshot still reports a disabled default as enabled", () => { |
| 136 | const entries = deriveProviderInstanceEntries([ |
| 137 | provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex" }), |
| 138 | ]); |
| 139 | const [entry] = applyProviderInstanceSettings( |
| 140 | entries, |
| 141 | { |
| 142 | providerInstances: { |
| 143 | [ProviderInstanceId.make("codex")]: { |
| 144 | driver: ProviderDriverKind.make("codex"), |
| 145 | enabled: false, |
| 146 | }, |
| 147 | }, |
| 148 | projectSettingsOverrides: {}, |
| 149 | }, |
| 150 | null, |
| 151 | ); |
| 152 | |
| 153 | expect(entry?.enabled).toBe(false); |
| 154 | }); |
| 155 | |
| 156 | it("treats a removed custom instance snapshot as disabled", () => { |
| 157 | const entries = deriveProviderInstanceEntries([ |
| 158 | provider({ |
| 159 | provider: ProviderDriverKind.make("claudeAgent"), |
| 160 | instanceId: "claude_work", |
| 161 | }), |
| 162 | ]); |
| 163 | const [entry] = applyProviderInstanceSettings( |
| 164 | entries, |
| 165 | { |
| 166 | providerInstances: {}, |
| 167 | projectSettingsOverrides: {}, |
| 168 | }, |
| 169 | null, |
| 170 | ); |
| 171 | |
| 172 | expect(entry?.enabled).toBe(false); |
| 173 | }); |
| 174 | |
| 175 | it.each(["constructor", "toString"])( |
| 176 | "treats a removed custom instance named %s as disabled", |
| 177 | (instanceId) => { |
| 178 | const entries = deriveProviderInstanceEntries([ |
| 179 | provider({ |
| 180 | provider: ProviderDriverKind.make("claudeAgent"), |
| 181 | instanceId, |
| 182 | }), |
| 183 | ]); |
| 184 | const [entry] = applyProviderInstanceSettings( |
| 185 | entries, |
| 186 | { |
| 187 | providerInstances: {}, |
| 188 | projectSettingsOverrides: {}, |
| 189 | }, |
| 190 | null, |
| 191 | ); |
| 192 | |
| 193 | expect(entry?.enabled).toBe(false); |
| 194 | }, |
| 195 | ); |
| 196 | |
| 197 | it("uses settings for a configured custom instance named constructor", () => { |
| 198 | const instanceId = ProviderInstanceId.make("constructor"); |
| 199 | const entries = deriveProviderInstanceEntries([ |
| 200 | provider({ |
| 201 | provider: ProviderDriverKind.make("claudeAgent"), |
| 202 | instanceId, |
| 203 | }), |
| 204 | ]); |
| 205 | const [entry] = applyProviderInstanceSettings( |
| 206 | entries, |
| 207 | { |
| 208 | providerInstances: { |
| 209 | [instanceId]: { |
| 210 | driver: ProviderDriverKind.make("claudeAgent"), |
| 211 | enabled: false, |
| 212 | }, |
| 213 | }, |
| 214 | projectSettingsOverrides: {}, |
| 215 | }, |
| 216 | null, |
| 217 | ); |
| 218 | |
| 219 | expect(entry?.enabled).toBe(false); |
| 220 | }); |
| 221 | |
| 222 | it("treats a removed default instance for a fork driver as disabled", () => { |
| 223 | const driver = ProviderDriverKind.make("constructor"); |
| 224 | const entries = deriveProviderInstanceEntries([ |
| 225 | provider({ |
| 226 | provider: driver, |
| 227 | instanceId: "constructor", |
| 228 | }), |
| 229 | ]); |
| 230 | const [entry] = applyProviderInstanceSettings( |
| 231 | entries, |
| 232 | { |
| 233 | providerInstances: {}, |
| 234 | projectSettingsOverrides: {}, |
| 235 | }, |
| 236 | null, |
| 237 | ); |
| 238 | |
| 239 | expect(entry?.isDefault).toBe(true); |
| 240 | expect(entry?.enabled).toBe(false); |
| 241 | }); |
| 242 | |
| 243 | it("uses the driver default for an unconfigured built-in default instance", () => { |
| 244 | const entries = deriveProviderInstanceEntries([ |
| 245 | provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex", enabled: false }), |
| 246 | provider({ provider: ProviderDriverKind.make("grok"), instanceId: "grok", enabled: true }), |
| 247 | ]); |
| 248 | const [codex, grok] = applyProviderInstanceSettings( |
| 249 | entries, |
| 250 | { |
| 251 | providerInstances: {}, |
| 252 | projectSettingsOverrides: {}, |
| 253 | }, |
| 254 | null, |
| 255 | ); |
| 256 | |
| 257 | // Settings decide over a stale probe: Codex starts on, Grok starts off. |
| 258 | expect(codex?.enabled).toBe(true); |
| 259 | expect(grok?.enabled).toBe(false); |
| 260 | }); |
| 261 | }); |
| 262 | |
| 263 | describe("deriveProviderInstanceEntries", () => { |
| 264 | it("uses explicit instance id and driver kind from the snapshot", () => { |
| 265 | const snapshot = provider({ |
| 266 | provider: ProviderDriverKind.make("codex"), |
| 267 | instanceId: "codex_personal", |
| 268 | }); |
| 269 | const [entry] = deriveProviderInstanceEntries([snapshot]); |
| 270 | |
| 271 | expect(entry?.instanceId).toBe("codex_personal"); |
| 272 | expect(entry?.driverKind).toBe("codex"); |
| 273 | expect(entry?.isDefault).toBe(false); |
| 274 | }); |
| 275 | }); |
| 276 | |
| 277 | describe("deriveProviderEntriesByEnvironment", () => { |
| 278 | it("resolves registry branding from each environment's settings", () => { |
| 279 | const instanceId = "custom-acp"; |
| 280 | const snapshot = provider({ provider: ProviderDriverKind.make("acpRegistry"), instanceId }); |
| 281 | const byEnvironment = deriveProviderEntriesByEnvironment( |
| 282 | ["devin", "other-agent"].map( |
| 283 | (agentId) => |
| 284 | [ |
| 285 | agentId, |
| 286 | [snapshot], |
| 287 | { |
| 288 | providerInstances: { |
| 289 | [instanceId]: { |
| 290 | driver: ProviderDriverKind.make("acpRegistry"), |
| 291 | enabled: true, |
| 292 | config: { agentId, registryIconUrl: `https://example.com/${agentId}.svg` }, |
| 293 | }, |
| 294 | }, |
| 295 | projectSettingsOverrides: {}, |
| 296 | }, |
| 297 | ] as const, |
| 298 | ), |
| 299 | ); |
| 300 | expect(byEnvironment.get("devin")?.get(instanceId)?.acpRegistryAgentId).toBe("devin"); |
| 301 | expect(byEnvironment.get("devin")?.get(instanceId)?.acpRegistryIconUrl).toBe( |
| 302 | "https://example.com/devin.svg", |
| 303 | ); |
| 304 | expect(byEnvironment.get("other-agent")?.get(instanceId)?.acpRegistryAgentId).toBe( |
| 305 | "other-agent", |
| 306 | ); |
| 307 | }); |
| 308 | |
| 309 | it("keeps same-id default instances distinct per environment", () => { |
| 310 | const byEnvironment = deriveProviderEntriesByEnvironment([ |
| 311 | [ |
| 312 | "local", |
| 313 | [ |
| 314 | provider({ |
| 315 | provider: ProviderDriverKind.make("claude"), |
| 316 | instanceId: "claude", |
| 317 | displayName: "Claude Local", |
| 318 | accentColor: "#112233", |
| 319 | }), |
| 320 | ], |
| 321 | ], |
| 322 | [ |
| 323 | "remote", |
| 324 | [ |
| 325 | provider({ |
| 326 | provider: ProviderDriverKind.make("claude"), |
| 327 | instanceId: "claude", |
| 328 | displayName: "Claude Remote", |
| 329 | accentColor: "#445566", |
| 330 | }), |
| 331 | ], |
| 332 | ], |
| 333 | ]); |
| 334 | |
| 335 | expect(byEnvironment.get("local")?.get("claude")?.displayName).toBe("Claude Local"); |
| 336 | expect(byEnvironment.get("local")?.get("claude")?.accentColor).toBe("#112233"); |
| 337 | expect(byEnvironment.get("remote")?.get("claude")?.displayName).toBe("Claude Remote"); |
| 338 | expect(byEnvironment.get("remote")?.get("claude")?.accentColor).toBe("#445566"); |
| 339 | }); |
| 340 | |
| 341 | it("never falls back to another environment's instances", () => { |
| 342 | const byEnvironment = deriveProviderEntriesByEnvironment([ |
| 343 | ["local", [provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex" })]], |
| 344 | ["empty", []], |
| 345 | ]); |
| 346 | |
| 347 | expect(byEnvironment.get("empty")?.get("codex")).toBeUndefined(); |
| 348 | // Every environment gets its own bucket, so an absent lookup is a real |
| 349 | // "this environment has no such instance", not a missing key. |
| 350 | expect(byEnvironment.get("empty")?.size).toBe(0); |
| 351 | }); |
| 352 | }); |
| 353 | |
| 354 | describe("resolveSelectableProviderInstance", () => { |
| 355 | it("returns the requested instance when it is enabled and available", () => { |
| 356 | const requested = ProviderInstanceId.make("claude_work"); |
| 357 | const providers = [ |
| 358 | provider({ provider: ProviderDriverKind.make("codex"), instanceId: "codex" }), |
| 359 | provider({ provider: ProviderDriverKind.make("claudeAgent"), instanceId: requested }), |
| 360 | ]; |
| 361 | |
| 362 | expect(resolveSelectableProviderInstance(providers, requested)).toBe(requested); |
| 363 | }); |
| 364 | |
| 365 | it("falls back to the first enabled and available instance", () => { |
| 366 | const disabled = ProviderInstanceId.make("codex"); |
| 367 | const fallback = ProviderInstanceId.make("claudeAgent"); |
| 368 | const providers = [ |
| 369 | provider({ |
| 370 | provider: ProviderDriverKind.make("codex"), |
| 371 | instanceId: disabled, |
| 372 | enabled: false, |
| 373 | }), |
| 374 | provider({ provider: ProviderDriverKind.make("claudeAgent"), instanceId: fallback }), |
| 375 | ]; |
| 376 | |
| 377 | expect(resolveSelectableProviderInstance(providers, disabled)).toBe(fallback); |
| 378 | }); |
| 379 | |
| 380 | it("prefers a ready instance over an enabled one whose driver cannot start", () => { |
| 381 | const notInstalled = ProviderInstanceId.make("codex"); |
| 382 | const ready = ProviderInstanceId.make("claudeAgent"); |
| 383 | const providers = [ |
| 384 | provider({ |
| 385 | provider: ProviderDriverKind.make("codex"), |
| 386 | instanceId: notInstalled, |
| 387 | status: "error", |
| 388 | }), |
| 389 | provider({ provider: ProviderDriverKind.make("claudeAgent"), instanceId: ready }), |
| 390 | ]; |
| 391 | |
| 392 | expect(resolveSelectableProviderInstance(providers, undefined)).toBe(ready); |
| 393 | }); |
| 394 | |
| 395 | it("prefers an unprobed (warning) instance over one whose probe errored", () => { |
| 396 | const notInstalled = ProviderInstanceId.make("codex"); |
| 397 | const unprobed = ProviderInstanceId.make("claudeAgent"); |
| 398 | const providers = [ |
| 399 | provider({ |
| 400 | provider: ProviderDriverKind.make("codex"), |
| 401 | instanceId: notInstalled, |
| 402 | status: "error", |
| 403 | }), |
| 404 | provider({ |
| 405 | provider: ProviderDriverKind.make("claudeAgent"), |
| 406 | instanceId: unprobed, |
| 407 | status: "warning", |
| 408 | }), |
| 409 | ]; |
| 410 | |
| 411 | expect(resolveSelectableProviderInstance(providers, undefined)).toBe(unprobed); |
| 412 | }); |
| 413 | |
| 414 | it("keeps a requested instance even when its probe errored", () => { |
| 415 | const requested = ProviderInstanceId.make("codex"); |
| 416 | const providers = [ |
| 417 | provider({ |
| 418 | provider: ProviderDriverKind.make("codex"), |
| 419 | instanceId: requested, |
| 420 | status: "error", |
| 421 | }), |
| 422 | provider({ provider: ProviderDriverKind.make("claudeAgent"), instanceId: "claudeAgent" }), |
| 423 | ]; |
| 424 | |
| 425 | expect(resolveSelectableProviderInstance(providers, requested)).toBe(requested); |
| 426 | }); |
| 427 | |
| 428 | it("does not invent an errored instance as a new-user default", () => { |
| 429 | const notInstalled = ProviderInstanceId.make("codex"); |
| 430 | const providers = [ |
| 431 | provider({ |
| 432 | provider: ProviderDriverKind.make("codex"), |
| 433 | instanceId: notInstalled, |
| 434 | status: "error", |
| 435 | }), |
| 436 | ]; |
| 437 | |
| 438 | expect(resolveSelectableProviderInstance(providers, undefined)).toBeUndefined(); |
| 439 | }); |
| 440 | |
| 441 | it("does not return disabled, unavailable, or unknown instances when none are sendable", () => { |
| 442 | const disabled = ProviderInstanceId.make("codex"); |
| 443 | const unavailable = ProviderInstanceId.make("claudeAgent"); |
| 444 | const unknown = ProviderInstanceId.make("removed_instance"); |
| 445 | const providers = [ |
| 446 | provider({ |
| 447 | provider: ProviderDriverKind.make("codex"), |
| 448 | instanceId: disabled, |
| 449 | enabled: false, |
| 450 | }), |
| 451 | provider({ |
| 452 | provider: ProviderDriverKind.make("claudeAgent"), |
| 453 | instanceId: unavailable, |
| 454 | availability: "unavailable", |
| 455 | }), |
| 456 | ]; |
| 457 | |
| 458 | expect(resolveSelectableProviderInstance(providers, disabled)).toBeUndefined(); |
| 459 | expect(resolveSelectableProviderInstance(providers, unavailable)).toBeUndefined(); |
| 460 | expect(resolveSelectableProviderInstance(providers, unknown)).toBeUndefined(); |
| 461 | }); |
| 462 | }); |
| 463 | |
| 464 | describe("getDefaultProviderInstanceModel", () => { |
| 465 | it("uses the instance's own models, not the default instance of the kind", () => { |
| 466 | const providers = [ |
| 467 | provider({ |
| 468 | provider: ProviderDriverKind.make("claudeAgent"), |
| 469 | instanceId: "claude_openrouter", |
| 470 | models: [model("openai/gpt-5.5", true), model("claude-opus-4-8")], |
| 471 | }), |
| 472 | provider({ |
| 473 | provider: ProviderDriverKind.make("claudeAgent"), |
| 474 | instanceId: "claudeAgent", |
| 475 | models: [model("claude-sonnet-5")], |
| 476 | }), |
| 477 | ]; |
| 478 | |
| 479 | expect( |
| 480 | getDefaultProviderInstanceModel(providers, ProviderInstanceId.make("claude_openrouter")), |
| 481 | ).toBe("claude-opus-4-8"); |
| 482 | }); |
| 483 | |
| 484 | it("falls back to the driver default when the instance reports no models", () => { |
| 485 | const providers = [ |
| 486 | provider({ provider: ProviderDriverKind.make("claudeAgent"), instanceId: "claudeAgent" }), |
| 487 | ]; |
| 488 | |
| 489 | const resolved = getDefaultProviderInstanceModel( |
| 490 | providers, |
| 491 | ProviderInstanceId.make("claudeAgent"), |
| 492 | ); |
| 493 | expect(typeof resolved).toBe("string"); |
| 494 | expect(resolved?.length).toBeGreaterThan(0); |
| 495 | }); |
| 496 | |
| 497 | it("honors the instance's declared default before model-list order", () => { |
| 498 | const providers = [ |
| 499 | provider({ |
| 500 | provider: ProviderDriverKind.make("claudeAgent"), |
| 501 | instanceId: "claudeAgent", |
| 502 | models: [model("claude-sonnet-5"), model("claude-opus-4-8", false, true)], |
| 503 | }), |
| 504 | ]; |
| 505 | |
| 506 | expect(getDefaultProviderInstanceModel(providers, ProviderInstanceId.make("claudeAgent"))).toBe( |
| 507 | "claude-opus-4-8", |
| 508 | ); |
| 509 | }); |
| 510 | |
| 511 | it("returns undefined for an unknown instance", () => { |
| 512 | expect( |
| 513 | getDefaultProviderInstanceModel([], ProviderInstanceId.make("removed_instance")), |
| 514 | ).toBeUndefined(); |
| 515 | }); |
| 516 | }); |
| 517 | |
| 518 | describe("resolveDefaultProviderModelSelection", () => { |
| 519 | it.each([ |
| 520 | ["codex", "codex", "gpt-5.6"], |
| 521 | ["claudeAgent", "claudeAgent", "claude-fable-5"], |
| 522 | ["cursor", "cursor", "composer-2"], |
| 523 | ])("uses the only available %s instance", (driver, instanceId, modelSlug) => { |
| 524 | const providers = [ |
| 525 | provider({ |
| 526 | provider: ProviderDriverKind.make(driver), |
| 527 | instanceId, |
| 528 | models: [model(modelSlug, false, true)], |
| 529 | }), |
| 530 | ]; |
| 531 | |
| 532 | expect(resolveDefaultProviderModelSelection(providers, null)).toEqual({ |
| 533 | instanceId, |
| 534 | model: modelSlug, |
| 535 | }); |
| 536 | }); |
| 537 | |
| 538 | it("preserves a valid stored selection including its options", () => { |
| 539 | const providers = [ |
| 540 | provider({ |
| 541 | provider: ProviderDriverKind.make("claudeAgent"), |
| 542 | instanceId: "claudeAgent", |
| 543 | models: [model("claude-opus-4-8")], |
| 544 | }), |
| 545 | ]; |
| 546 | const stored = { |
| 547 | instanceId: ProviderInstanceId.make("claudeAgent"), |
| 548 | model: "custom-model", |
| 549 | options: [{ id: "effort", value: "high" }], |
| 550 | }; |
| 551 | |
| 552 | expect(resolveDefaultProviderModelSelection(providers, stored)).toBe(stored); |
| 553 | }); |
| 554 | |
| 555 | it("replaces a stale stored instance with the first ready instance and its model", () => { |
| 556 | const providers = [ |
| 557 | provider({ |
| 558 | provider: ProviderDriverKind.make("codex"), |
| 559 | instanceId: "codex", |
| 560 | status: "warning", |
| 561 | models: [model("gpt-5.6")], |
| 562 | }), |
| 563 | provider({ |
| 564 | provider: ProviderDriverKind.make("claudeAgent"), |
| 565 | instanceId: "claudeAgent", |
| 566 | models: [model("claude-opus-4-8", false, true)], |
| 567 | }), |
| 568 | ]; |
| 569 | |
| 570 | expect( |
| 571 | resolveDefaultProviderModelSelection(providers, { |
| 572 | instanceId: ProviderInstanceId.make("removed-provider"), |
| 573 | model: "stale-model", |
| 574 | }), |
| 575 | ).toEqual({ instanceId: "claudeAgent", model: "claude-opus-4-8" }); |
| 576 | }); |
| 577 | |
| 578 | it.each([{ enabled: false }, { availability: "unavailable" as const }])( |
| 579 | "replaces an unavailable stored instance deterministically", |
| 580 | (requestedState) => { |
| 581 | const providers = [ |
| 582 | provider({ |
| 583 | provider: ProviderDriverKind.make("codex"), |
| 584 | instanceId: "codex", |
| 585 | models: [model("gpt-5.6")], |
| 586 | ...requestedState, |
| 587 | }), |
| 588 | provider({ |
| 589 | provider: ProviderDriverKind.make("claudeAgent"), |
| 590 | instanceId: "claudeAgent", |
| 591 | models: [model("claude-opus-4-8", false, true)], |
| 592 | }), |
| 593 | ]; |
| 594 | |
| 595 | expect( |
| 596 | resolveDefaultProviderModelSelection(providers, { |
| 597 | instanceId: ProviderInstanceId.make("codex"), |
| 598 | model: "gpt-5.6", |
| 599 | }), |
| 600 | ).toEqual({ instanceId: "claudeAgent", model: "claude-opus-4-8" }); |
| 601 | }, |
| 602 | ); |
| 603 | |
| 604 | it("returns no selection for empty, disabled, unavailable, or error-only profiles", () => { |
| 605 | expect(resolveDefaultProviderModelSelection([], null)).toBeNull(); |
| 606 | expect( |
| 607 | resolveDefaultProviderModelSelection( |
| 608 | [ |
| 609 | provider({ |
| 610 | provider: ProviderDriverKind.make("codex"), |
| 611 | instanceId: "codex", |
| 612 | enabled: false, |
| 613 | }), |
| 614 | ], |
| 615 | null, |
| 616 | ), |
| 617 | ).toBeNull(); |
| 618 | expect( |
| 619 | resolveDefaultProviderModelSelection( |
| 620 | [ |
| 621 | provider({ |
| 622 | provider: ProviderDriverKind.make("codex"), |
| 623 | instanceId: "codex", |
| 624 | availability: "unavailable", |
| 625 | }), |
| 626 | ], |
| 627 | null, |
| 628 | ), |
| 629 | ).toBeNull(); |
| 630 | expect( |
| 631 | resolveDefaultProviderModelSelection( |
| 632 | [ |
| 633 | provider({ |
| 634 | provider: ProviderDriverKind.make("codex"), |
| 635 | instanceId: "codex", |
| 636 | status: "error", |
| 637 | }), |
| 638 | ], |
| 639 | null, |
| 640 | ), |
| 641 | ).toBeNull(); |
| 642 | }); |
| 643 | }); |
| 644 | |
| 645 | describe("provider icon metadata", () => { |
| 646 | it("clears stale registry branding when an instance switches to a local command", () => { |
| 647 | const instanceId = ProviderInstanceId.make("custom-acp"); |
| 648 | const driver = ProviderDriverKind.make("acpRegistry"); |
| 649 | const iconUrl = "https://cdn.agentclientprotocol.com/registry/icons/swe-agent.svg"; |
| 650 | const snapshots = deriveProviderInstanceEntries([ |
| 651 | { ...provider({ provider: driver, instanceId }), iconUrl }, |
| 652 | ]); |
| 653 | const registryConfig = { agentId: "swe-agent", registryIconUrl: iconUrl }; |
| 654 | const brandedEntries = applyProviderInstanceSettings( |
| 655 | snapshots, |
| 656 | { |
| 657 | providerInstances: { [instanceId]: { driver, enabled: true, config: registryConfig } }, |
| 658 | projectSettingsOverrides: {}, |
| 659 | }, |
| 660 | null, |
| 661 | ); |
| 662 | expect(brandedEntries[0]?.acpRegistryAgentId).toBe("swe-agent"); |
| 663 | expect(brandedEntries[0]?.acpRegistryIconUrl).toBe(iconUrl); |
| 664 | |
| 665 | const [localEntry] = applyProviderInstanceSettings( |
| 666 | brandedEntries, |
| 667 | { |
| 668 | providerInstances: { |
| 669 | [instanceId]: { |
| 670 | driver, |
| 671 | enabled: false, |
| 672 | config: { ...registryConfig, source: "local", commandPath: "dsh" }, |
| 673 | }, |
| 674 | }, |
| 675 | projectSettingsOverrides: {}, |
| 676 | }, |
| 677 | null, |
| 678 | ); |
| 679 | expect(localEntry?.acpRegistryAgentId).toBeUndefined(); |
| 680 | expect(localEntry?.acpRegistryIconUrl).toBeUndefined(); |
| 681 | expect(localEntry?.enabled).toBe(false); |
| 682 | expect(localEntry?.snapshot).toBe(snapshots[0]?.snapshot); |
| 683 | |
| 684 | const [restoredEntry] = applyProviderInstanceSettings( |
| 685 | localEntry ? [localEntry] : [], |
| 686 | { |
| 687 | providerInstances: { [instanceId]: { driver, enabled: true, config: registryConfig } }, |
| 688 | projectSettingsOverrides: {}, |
| 689 | }, |
| 690 | null, |
| 691 | ); |
| 692 | expect(restoredEntry?.acpRegistryAgentId).toBe("swe-agent"); |
| 693 | expect(restoredEntry?.acpRegistryIconUrl).toBe(iconUrl); |
| 694 | expect(restoredEntry?.enabled).toBe(true); |
| 695 | }); |
| 696 | |
| 697 | it("retains server-published registry icons without local settings", () => { |
| 698 | const iconUrl = "https://cdn.agentclientprotocol.com/registry/icons/swe-agent.svg"; |
| 699 | const [entry] = deriveProviderInstanceEntries([ |
| 700 | { |
| 701 | ...provider({ provider: ProviderDriverKind.make("acpRegistry"), instanceId: "swe-remote" }), |
| 702 | iconUrl, |
| 703 | }, |
| 704 | ]); |
| 705 | expect(entry?.acpRegistryIconUrl).toBe(iconUrl); |
| 706 | expect(entry?.driverKind).toBe("acpRegistry"); |
| 707 | }); |
| 708 | }); |