feat: move goose2 provider catalog behind ACP layer (#9030)

Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Matt Toohey <contact@matttoohey.com>
This commit is contained in:
Kalvin C
2026-05-05 18:36:19 -07:00
committed by GitHub
parent 8464718588
commit b17fd4f287
56 changed files with 3235 additions and 895 deletions
@@ -0,0 +1,29 @@
import { beforeEach, describe, expect, it } from "vitest";
import { useProviderCatalogStore } from "@/features/providers/stores/providerCatalogStore";
import { discoverAcpProvidersFromEntries } from "../acp";
describe("discoverAcpProvidersFromEntries", () => {
beforeEach(() => {
useProviderCatalogStore.getState().reset();
});
it("preserves agent inventory entries when the setup catalog has not loaded", () => {
expect(
discoverAcpProvidersFromEntries([
{
providerId: "codex-acp",
providerName: "Codex",
category: "agent",
},
{
providerId: "openai",
providerName: "OpenAI",
category: "model",
},
]),
).toEqual([
{ id: "goose", label: "Goose" },
{ id: "codex-acp", label: "Codex" },
]);
});
});
+6 -5
View File
@@ -44,7 +44,7 @@ export async function discoverAcpProviders(): Promise<AcpProvider[]> {
* avoiding a duplicate `_goose/providers/list` RPC.
*/
export function discoverAcpProvidersFromEntries(
entries: Array<{ providerId: string; providerName: string }>,
entries: Parameters<typeof directAcp.buildProviderListFromEntries>[0],
): AcpProvider[] {
return resolveProvidersCatalog(
directAcp.buildProviderListFromEntries(entries),
@@ -60,13 +60,14 @@ function resolveProvidersCatalog(providers: AcpProvider[]): AcpProvider[] {
provider.id,
provider.label,
);
if (!catalogId || seen.has(catalogId)) {
const resolvedId = catalogId ?? provider.id;
if (seen.has(resolvedId)) {
return null;
}
seen.add(catalogId);
seen.add(resolvedId);
return {
id: catalogId,
label: getCatalogEntry(catalogId)?.displayName ?? provider.label,
id: resolvedId,
label: getCatalogEntry(resolvedId)?.displayName ?? provider.label,
};
})
.filter((provider): provider is AcpProvider => provider !== null);
+5 -1
View File
@@ -5,6 +5,7 @@ import type {
PromptResponse,
SessionInfo,
} from "@agentclientprotocol/sdk";
import type { ProviderInventoryEntryDto } from "@aaif/goose-sdk";
import { getClient } from "./acpConnection";
import { perfLog } from "@/shared/lib/perfLog";
@@ -46,12 +47,15 @@ export const DEFAULT_PROVIDER: AcpProvider = {
* already-fetched entries at startup).
*/
export function buildProviderListFromEntries(
entries: Array<{ providerId: string; providerName: string }>,
entries: Array<
Pick<ProviderInventoryEntryDto, "providerId" | "providerName" | "category">
>,
): AcpProvider[] {
return [
DEFAULT_PROVIDER,
...entries
.filter((entry) => !DEPRECATED_PROVIDER_IDS.has(entry.providerId))
.filter((entry) => entry.category === "agent")
.map((entry) => ({ id: entry.providerId, label: entry.providerName })),
];
}
@@ -332,6 +332,9 @@
"useTemplateDescription": "Start with endpoint and model defaults for a known provider."
}
},
"catalog": {
"loading": "Loading provider catalog..."
},
"disconnect": "Disconnect",
"models": {
"description": "AI models power your agents. Goose requires one to work, but some agents bring their own.",
@@ -332,6 +332,9 @@
"useTemplateDescription": "Comienza con valores predeterminados de endpoint y modelos para un proveedor conocido."
}
},
"catalog": {
"loading": "Cargando catálogo de proveedores..."
},
"disconnect": "Desconectar",
"models": {
"description": "Necesitas al menos un proveedor de modelos para usar el agente Goose. Algunos agentes pueden traer sus propias conexiones de modelo.",
+33 -37
View File
@@ -1,26 +1,15 @@
export type ProviderCategory = "agent" | "model";
import type {
ProviderSetupCatalogEntryDto,
ProviderSetupCategoryDto,
ProviderSetupFieldDto,
ProviderSetupMethodDto,
ProviderSetupGroupDto,
} from "@aaif/goose-sdk";
export type ProviderSetupMethod =
| "none"
| "single_api_key"
| "config_fields"
| "host_with_oauth_fallback"
| "oauth_browser"
| "oauth_device_code"
| "cloud_credentials"
| "local"
| "cli_auth";
export type ProviderTier = "promoted" | "standard" | "advanced";
export interface ProviderField {
key: string;
label: string;
secret: boolean;
required: boolean;
placeholder?: string;
defaultValue?: string;
}
export type ProviderCategory = ProviderSetupCategoryDto;
export type ProviderSetupMethod = ProviderSetupMethodDto;
export type ProviderGroup = ProviderSetupGroupDto;
export type ProviderField = ProviderSetupFieldDto;
export interface ProviderFieldValue {
key: string;
@@ -30,23 +19,30 @@ export interface ProviderFieldValue {
required: boolean;
}
export interface ProviderCatalogEntry {
export type ProviderCatalogEntry = Omit<
ProviderSetupCatalogEntryDto,
| "providerId"
| "name"
| "nativeConnectQuery"
| "binaryName"
| "docUrl"
| "showOnlyWhenInstalled"
| "supportsInstall"
| "supportsAuth"
| "supportsAuthStatus"
> & {
id: string;
displayName: string;
category: ProviderCategory;
description: string;
setupMethod: ProviderSetupMethod;
nativeConnectQuery?: string;
envVar?: string;
fields?: ProviderField[];
binaryName?: string;
installCommand?: string;
authCommand?: string;
authStatusCommand?: string;
docsUrl?: string;
tier: ProviderTier;
showOnlyWhenInstalled?: boolean;
}
nativeConnectQuery?: NonNullable<
ProviderSetupCatalogEntryDto["nativeConnectQuery"]
>;
binaryName?: NonNullable<ProviderSetupCatalogEntryDto["binaryName"]>;
docsUrl?: NonNullable<ProviderSetupCatalogEntryDto["docUrl"]>;
showOnlyWhenInstalled?: ProviderSetupCatalogEntryDto["showOnlyWhenInstalled"];
supportsInstall?: ProviderSetupCatalogEntryDto["supportsInstall"];
supportsAuth?: ProviderSetupCatalogEntryDto["supportsAuth"];
supportsAuthStatus?: ProviderSetupCatalogEntryDto["supportsAuthStatus"];
};
export type ProviderSetupStatus =
| "built_in"