fix(paseo-agent): support empty model-provider setup

Allow catalog provider setup to persist credentials before explicit model overrides exist, while keeping unrelated provider snapshots warmable after Paseo Agent config changes.
This commit is contained in:
Mohamed Boudra
2026-07-02 13:12:23 +02:00
parent d6e4e5f3ce
commit 5d67032ef6
17 changed files with 441 additions and 209 deletions
@@ -1020,6 +1020,65 @@ describe("ProviderSnapshotManager applyMutableProviderConfig", () => {
});
describe("ProviderSnapshotManager applyPaseoAgentConfig", () => {
test("keeps unrelated provider loading state when Paseo Agent config changes", async () => {
let resolveFetchStarted: (() => void) | undefined;
let releaseFetch: (() => void) | undefined;
const fetchStarted = new Promise<void>((resolveStarted) => {
resolveFetchStarted = resolveStarted;
});
const fetchRelease = new Promise<void>((resolveRelease) => {
releaseFetch = resolveRelease;
});
const manager = new ProviderSnapshotManager({
logger: createTestLogger(),
providerOverrides: {
codex: { enabled: false },
copilot: { enabled: false },
opencode: { enabled: false },
pi: { enabled: false },
omp: { enabled: false },
},
extraClients: {
claude: createExtraClient("claude", {
async isAvailable() {
return true;
},
async fetchCatalog() {
resolveFetchStarted?.();
await fetchRelease;
return { models: [] as AgentModelDefinition[], modes: [] as AgentMode[] };
},
}),
},
paseoAgentConfig: {},
});
try {
manager.getSnapshot();
await fetchStarted;
manager.applyPaseoAgentConfig({
providers: {
"openrouter-main": {
type: "openrouter",
options: {
apiKey: "sk-test",
models: [{ id: "anthropic/claude-3.7-sonnet", label: "Claude" }],
},
},
},
});
expect(manager.getSnapshot().find((entry) => entry.provider === "claude")).toMatchObject({
provider: "claude",
status: "loading",
enabled: true,
});
} finally {
releaseFetch?.();
manager.destroy();
}
});
test("refreshes Paseo Agent models without daemon restart", async () => {
const manager = new ProviderSnapshotManager({
logger: createTestLogger(),
@@ -585,7 +585,7 @@ export class ProviderSnapshotManager {
defaultModeId: definition?.defaultModeId ?? null,
};
if (!definition?.enabled || !current || current.status === "loading") {
if (!definition?.enabled) {
entries.set(provider, {
...metadata,
status: "unavailable",
@@ -594,6 +594,14 @@ export class ProviderSnapshotManager {
continue;
}
if (!current || current.status === "loading") {
entries.set(provider, {
...metadata,
status: "loading",
});
continue;
}
entries.set(provider, {
...current,
...metadata,
@@ -110,6 +110,27 @@ describe("PaseoAgentConfigService", () => {
expect(JSON.stringify(presentService.getProviders())).not.toContain("sk-env-secret");
});
test("reports configured API-key providers as available even without exposed models", () => {
const service = new PaseoAgentConfigService({
paseoHome: home,
logger: createTestLogger(),
});
service.setProvider({
name: "openrouter",
providerType: "openrouter",
options: {
apiKey: "sk-test",
},
});
expect(service.getProviders().providers[0]).toMatchObject({
auth: { kind: "api_key", configured: true, source: "literal" },
models: [],
available: true,
});
});
test("rejects an unknown provider type with a clear error and persists nothing", () => {
const service = new PaseoAgentConfigService({
paseoHome: home,
@@ -165,9 +165,9 @@ function redactedProviders(
const provider: RedactedPaseoAgentProviderConfig = {
name,
providerType: catalogEntry.id,
models: models.map((model) => ({ ...model })),
models: models.map((model) => Object.assign({}, model)),
auth,
available: auth.configured && models.length > 0,
available: auth.configured,
error: null,
};
provider.baseUrl = settings.baseUrl;