From a6bbc8aa223135cd7025bb3b0e251660f3fef555 Mon Sep 17 00:00:00 2001 From: mrohan-sq Date: Thu, 3 Sep 2026 08:14:12 -0500 Subject: [PATCH 1/6] Add Pi as a curated agent harness provider Uncomment the pi-acp entry in the curated provider catalog so Pi appears as a selectable harness. Remove supportsModelList/modelSelectionHint so Berd queries pi-acp's ACP config options for available models (including Databricks via the pi-databricks extension). supportsInstall stays false for now; one-click install will follow once the builderbot doctor crate carries Pi's install commands. --- src/features/providers/curatedProviders.ts | 29 ++++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/features/providers/curatedProviders.ts b/src/features/providers/curatedProviders.ts index 7b4806910..edc480f39 100644 --- a/src/features/providers/curatedProviders.ts +++ b/src/features/providers/curatedProviders.ts @@ -80,22 +80,19 @@ export const CURATED_PROVIDER_CATALOG: ProviderCatalogEntry[] = [ supportsAuth: true, supportsAuthStatus: true, }, - // full pi support in a future update - // { - // id: "pi-acp", - // displayName: "Pi", - // category: "agent", - // description: "Pi ACP agent", - // setupMethod: "cli_auth", - // binaryName: "pi-acp", - // group: "default", - // aliases: ["pi-acp", "pi"], - // supportsInstall: false, - // supportsAuth: false, - // supportsAuthStatus: false, - // supportsModelList: false, - // modelSelectionHint: "Use the Pi CLI to configure the model.", - // }, + { + id: "pi-acp", + displayName: "Pi", + category: "agent", + description: "Pi coding agent", + setupMethod: "cli_auth", + binaryName: "pi-acp", + group: "default", + aliases: ["pi-acp", "pi"], + supportsInstall: false, + supportsAuth: false, + supportsAuthStatus: false, + }, { id: "databricks_v2", displayName: "Databricks AI Gateway", From 66aa0eaa3eba8265e95360588597b0287ad57f38 Mon Sep 17 00:00:00 2001 From: mrohan-sq Date: Thu, 3 Sep 2026 08:32:06 -0500 Subject: [PATCH 2/6] Add Pi icon from pi.dev press kit --- src/shared/ui/icons/ProviderIcons.tsx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/shared/ui/icons/ProviderIcons.tsx b/src/shared/ui/icons/ProviderIcons.tsx index c3e70aaba..5ff7013c6 100644 --- a/src/shared/ui/icons/ProviderIcons.tsx +++ b/src/shared/ui/icons/ProviderIcons.tsx @@ -420,6 +420,30 @@ export function CopilotIcon({ className = "" }: { className?: string }) { ); } +export function PiIcon({ className = "" }: { className?: string }) { + return ( + + ); +} + const PROVIDER_ICON_MAP: Record ReactNode> = { goose: (className) => ( @@ -451,6 +475,8 @@ const PROVIDER_ICON_MAP: Record ReactNode> = { snowflake: (className) => , xai: (className) => , lmstudio: (className) => , + "pi-acp": (className) => , + pi: (className) => , }; function normalizeProviderId(providerId: string) { From 870e827cefd6692c2d639be9304793cde918e4ed Mon Sep 17 00:00:00 2001 From: mrohan-sq Date: Thu, 3 Sep 2026 08:38:21 -0500 Subject: [PATCH 3/6] fixup: format Pi icon path to satisfy biome --- src/shared/ui/icons/ProviderIcons.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/shared/ui/icons/ProviderIcons.tsx b/src/shared/ui/icons/ProviderIcons.tsx index 5ff7013c6..dfce90d13 100644 --- a/src/shared/ui/icons/ProviderIcons.tsx +++ b/src/shared/ui/icons/ProviderIcons.tsx @@ -436,10 +436,7 @@ export function PiIcon({ className = "" }: { className?: string }) { fillRule="evenodd" d="M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z" /> - + ); } From 85892bf66ba2acc0de33b4a16ae6f62f565eefd5 Mon Sep 17 00:00:00 2001 From: mrohan-sq Date: Thu, 3 Sep 2026 09:15:20 -0500 Subject: [PATCH 4/6] fixup: update provider catalog tests for pi-acp entry --- src/features/providers/hooks/useProviderModels.test.tsx | 2 ++ src/features/providers/providerCatalog.test.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/features/providers/hooks/useProviderModels.test.tsx b/src/features/providers/hooks/useProviderModels.test.tsx index f0a1da316..06ead6080 100644 --- a/src/features/providers/hooks/useProviderModels.test.tsx +++ b/src/features/providers/hooks/useProviderModels.test.tsx @@ -127,6 +127,7 @@ describe("useProviderModels", () => { "codex-acp", "copilot-acp", "cursor-agent", + "pi-acp", ]); act(() => { @@ -151,6 +152,7 @@ describe("useProviderModels", () => { "codex-acp", "copilot-acp", "cursor-agent", + "pi-acp", ]); act(() => { diff --git a/src/features/providers/providerCatalog.test.ts b/src/features/providers/providerCatalog.test.ts index 1214f3f7d..65426d45e 100644 --- a/src/features/providers/providerCatalog.test.ts +++ b/src/features/providers/providerCatalog.test.ts @@ -78,6 +78,7 @@ describe("provider catalog selectors", () => { "copilot-acp", "amp-acp", "cursor-agent", + "pi-acp", ]); expect(getModelProviders().map((provider) => provider.id)).toEqual([ "databricks_v2", From 65f6d068b03dd27d9a2e6096c8ac216a3569b340 Mon Sep 17 00:00:00 2001 From: mrohan-sq Date: Thu, 3 Sep 2026 10:01:40 -0500 Subject: [PATCH 5/6] fixup! fixup: update provider catalog tests for pi-acp entry --- src/shared/api/__tests__/acp.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/api/__tests__/acp.test.ts b/src/shared/api/__tests__/acp.test.ts index 8bed6a1d4..9e36ddf81 100644 --- a/src/shared/api/__tests__/acp.test.ts +++ b/src/shared/api/__tests__/acp.test.ts @@ -1094,6 +1094,7 @@ describe("acpCreateSession", () => { "copilot-acp", "amp-acp", "cursor-agent", + "pi-acp", ])("keeps the %s harness outside Goose provider policy", async (harnessId) => { await setRuntimeConfig(managedRuntimeConfig); mockNewSession.mockResolvedValue({ sessionId: `session-${harnessId}` }); From a945cff0a66bcd040b50b01130ebec8db477b3bd Mon Sep 17 00:00:00 2001 From: mrohan-sq Date: Thu, 3 Sep 2026 17:23:21 -0500 Subject: [PATCH 6/6] fix(chat): widen model picker for harness agents Harness agents (pi-acp, codex-acp, etc.) report long ACP model names that truncate in the fixed 224px model column. Widen the picker when their agent column is visible (560px, 736px with a reasoning-effort column) and let the model column flex. Goose keeps the compact layout. Add title tooltips on model names. --- src/features/chat/ui/AgentModelPicker.tsx | 34 ++++++- .../chat/ui/AgentModelPickerLists.tsx | 5 +- .../ui/__tests__/AgentModelPicker.test.tsx | 90 +++++++++++++++++++ 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/src/features/chat/ui/AgentModelPicker.tsx b/src/features/chat/ui/AgentModelPicker.tsx index 2adfbb91d..e76afafd1 100644 --- a/src/features/chat/ui/AgentModelPicker.tsx +++ b/src/features/chat/ui/AgentModelPicker.tsx @@ -80,6 +80,13 @@ type PopoverContentAlign = NonNullable< >; const REASONING_EFFORT_COLUMN_TRANSITION_MS = 240; const PICKER_WIDTH_COMPACT_PX = 420; +// Harness agents (pi-acp, codex-acp, etc.) can report long ACP model names +// (e.g. provider/id names), so they get wider pickers than Goose, whose +// curated model names are short. With a reasoning-effort column present, +// the agent (11.75rem) + reasoning (11rem) columns alone consume ~23rem, +// so the picker needs extra width for the model column to breathe. +const PICKER_WIDTH_HARNESS_PX = 560; +const PICKER_WIDTH_HARNESS_EXPANDED_PX = 736; const PICKER_WIDTH_EXPANDED_PX = 596; function toSentenceCaseLabel(value: string | undefined): string { @@ -395,10 +402,19 @@ export function AgentModelPicker({ !modelBrowsing && (agents.length > 1 || hasAgentNeedingSetup); const showReasoningEffortColumn = showReasoningEffort; + // Harness agents get wider pickers (and flexible model columns) whenever + // their agent column is visible, with or without a reasoning-effort + // column, so long ACP model names are not truncated. Goose is excluded + // because its curated model names are short. + const widenForHarnessAgent = showAgentColumn && selectedAgentId !== "goose"; const isWidePicker = showReasoningEffortColumn && showAgentColumn; const pickerWidth = isWidePicker - ? PICKER_WIDTH_EXPANDED_PX - : PICKER_WIDTH_COMPACT_PX; + ? widenForHarnessAgent + ? PICKER_WIDTH_HARNESS_EXPANDED_PX + : PICKER_WIDTH_EXPANDED_PX + : widenForHarnessAgent + ? PICKER_WIDTH_HARNESS_PX + : PICKER_WIDTH_COMPACT_PX; // Land keyboard focus in the revealed column, since the reveal button that // held focus unmounts with it. @@ -500,7 +516,13 @@ export function AgentModelPicker({ // gated single-column layout has no dead vertical space below the // model list. "flex max-h-[min(24rem,50vh)] flex-col overflow-hidden p-1 transition-[width] duration-[240ms] ease-[cubic-bezier(0.2,0,0,1)]", - isWidePicker ? "w-[37.25rem]" : "w-[26.25rem]", + isWidePicker + ? widenForHarnessAgent + ? "w-[46rem]" + : "w-[37.25rem]" + : widenForHarnessAgent + ? "w-[35rem]" + : "w-[26.25rem]", )} onInteractOutside={(event) => { classifyOutsideInteraction(event.target); @@ -672,7 +694,11 @@ export function AgentModelPicker({ data-col="model" className={cn( "flex min-h-0 min-w-0 overflow-hidden p-1", - showAgentColumn ? "ml-1 w-56 shrink-0" : "flex-1", + showAgentColumn + ? widenForHarnessAgent + ? "ml-1 flex-1" + : "ml-1 w-56 shrink-0" + : "flex-1", )} > {modelsLoading ? ( diff --git a/src/features/chat/ui/AgentModelPickerLists.tsx b/src/features/chat/ui/AgentModelPickerLists.tsx index 7ad2902bd..22a492462 100644 --- a/src/features/chat/ui/AgentModelPickerLists.tsx +++ b/src/features/chat/ui/AgentModelPickerLists.tsx @@ -399,7 +399,10 @@ export const RecommendedModelList = forwardRef< {providerIcon} ) : null} -
+
{getModelDisplayName(model)}
diff --git a/src/features/chat/ui/__tests__/AgentModelPicker.test.tsx b/src/features/chat/ui/__tests__/AgentModelPicker.test.tsx index 056f71e6f..5c9c980aa 100644 --- a/src/features/chat/ui/__tests__/AgentModelPicker.test.tsx +++ b/src/features/chat/ui/__tests__/AgentModelPicker.test.tsx @@ -52,6 +52,67 @@ describe("AgentModelPicker", () => { ).toHaveTextContent("GPT-4o"); }); + // Harness agents (pi-acp, codex-acp, etc.) get a wider picker in the + // new-chat composer because their ACP model names can be long; Goose keeps + // the compact layout. + it("widens the new-chat picker for a harness agent without reasoning effort", async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click( + screen.getByRole("button", { name: /choose agent and model/i }), + ); + + expect(screen.getByRole("dialog")).toHaveClass("w-[35rem]"); + }); + + // With a reasoning-effort column present the harness picker grows further, + // because the agent (11.75rem) and reasoning (11rem) columns alone consume + // most of the Goose expanded width and the model column needs room for + // long ACP names (e.g. "databricks / databricks-glm-5-3"). + it("widens the expanded picker for a harness agent with reasoning effort", async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click( + screen.getByRole("button", { name: /choose agent and model/i }), + ); + + expect(screen.getByRole("dialog")).toHaveClass("w-[46rem]"); + }); + it("routes not-ready Goose to Providers settings with a connect action", async () => { const user = userEvent.setup(); const onAgentChange = vi.fn(); @@ -1418,6 +1479,35 @@ describe("AgentModelPicker", () => { expect(content).toHaveClass("w-[37.25rem]"); }); + // Without a reasoning-effort column the gated reveal widens to the + // harness width so long model names (e.g. pi-acp provider/id names) are + // not truncated. Goose is excluded, so use a harness agent here. + it("widens to the harness width when no reasoning-effort column is present", async () => { + const user = userEvent.setup(); + + render( + , + ); + + await openPicker(user); + + const content = document.querySelector('[data-slot="popover-content"]'); + expect(content).toHaveClass("w-[26.25rem]"); + + await user.click(screen.getByRole("button", { name: /switch agent/i })); + + expect(content).toHaveClass("w-[35rem]"); + }); + it("hides the switch-agent button when the only agent is ready", async () => { const user = userEvent.setup(); renderGated({ agents: [{ id: "goose", label: "Goose" }] });