diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx index 81bc3948a..09b2a98a4 100644 --- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx +++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx @@ -98,8 +98,8 @@ type AgentDefinitionDialogProps = { input: CreatePersonaInput | UpdatePersonaInput, options: AgentDefinitionSubmitOptions, ) => Promise; - /** Offers publishing alongside Save after a shared agent has been edited. */ - showPublishUpdatesOption?: boolean; + /** Publishes saved changes when the edited agent is shared in the catalog. */ + publishCatalogUpdatesOnSave?: boolean; /** Rendered below the form fields in create mode only ("Where to run"). */ createRunSection?: React.ReactNode; /** Extra create-mode submit gate (e.g. incomplete provider config). */ @@ -127,7 +127,7 @@ export function AgentDefinitionDialog({ runtimesLoading = false, onOpenChange, onSubmit, - showPublishUpdatesOption = false, + publishCatalogUpdatesOnSave = false, createRunSection, createSubmitBlocked = false, }: AgentDefinitionDialogProps) { @@ -166,8 +166,6 @@ export function AgentDefinitionDialog({ const [isAvatarUploadPending, setIsAvatarUploadPending] = React.useState(false); const [hasUserChanges, setHasUserChanges] = React.useState(false); - const [publishUpdatesChecked, setPublishUpdatesChecked] = - React.useState(false); const { globalConfig, inheritedDefaults: { @@ -224,7 +222,6 @@ export function AgentDefinitionDialog({ setShowAdvancedFields(false); setIsAvatarUploadPending(false); setHasUserChanges(false); - setPublishUpdatesChecked(false); isRuntimeAutoSeededRef.current = false; hasSeededForOpenRef.current = false; }, [initialValues, open]); @@ -271,7 +268,6 @@ export function AgentDefinitionDialog({ setShowAdvancedFields(false); setIsAvatarUploadPending(false); setHasUserChanges(false); - setPublishUpdatesChecked(false); // isRuntimeAutoSeededRef and hasSeededForOpenRef are NOT reset here — the // [initialValues, open] effect resets both when the dialog re-opens. } @@ -329,8 +325,7 @@ export function AgentDefinitionDialog({ ...baseInput, }, { - publishCatalogUpdates: - showPublishUpdatesOption && hasUserChanges && publishUpdatesChecked, + publishCatalogUpdates: publishCatalogUpdatesOnSave && hasUserChanges, }, ); return; @@ -736,9 +731,9 @@ export function AgentDefinitionDialog({ isAvatarUploadPending={isAvatarUploadPending} isPending={isPending} onCancel={() => handleOpenChange(false)} - onPublishUpdatesCheckedChange={setPublishUpdatesChecked} - publishUpdatesChecked={publishUpdatesChecked} - showPublishUpdates={showPublishUpdatesOption && hasUserChanges} + publishesCatalogUpdates={ + publishCatalogUpdatesOnSave && hasUserChanges + } submitBlockReason={displayName.trim() ? submitBlockReason : null} submitLabel={submitLabel} /> diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialogFooter.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialogFooter.tsx index 2c7adc741..92428ad95 100644 --- a/desktop/src/features/agents/ui/AgentDefinitionDialogFooter.tsx +++ b/desktop/src/features/agents/ui/AgentDefinitionDialogFooter.tsx @@ -1,14 +1,11 @@ import { Button } from "@/shared/ui/button"; -import { Checkbox } from "@/shared/ui/checkbox"; type AgentDefinitionDialogFooterProps = { canSubmit: boolean; isAvatarUploadPending: boolean; isPending: boolean; onCancel: () => void; - onPublishUpdatesCheckedChange: (checked: boolean) => void; - publishUpdatesChecked: boolean; - showPublishUpdates: boolean; + publishesCatalogUpdates: boolean; submitBlockReason: string | null; submitLabel: string; }; @@ -18,9 +15,7 @@ export function AgentDefinitionDialogFooter({ isAvatarUploadPending, isPending, onCancel, - onPublishUpdatesCheckedChange, - publishUpdatesChecked, - showPublishUpdates, + publishesCatalogUpdates, submitBlockReason, submitLabel, }: AgentDefinitionDialogFooterProps) { @@ -35,22 +30,14 @@ export function AgentDefinitionDialogFooter({ {submitBlockReason}

) : null} - {showPublishUpdates ? ( - + This agent is in the community catalog. Your changes will be + published when you save. +

) : null} @@ -73,7 +60,9 @@ export function AgentDefinitionDialogFooter({ ? "Saving..." : isAvatarUploadPending ? "Uploading..." - : submitLabel} + : publishesCatalogUpdates + ? "Save and publish" + : submitLabel} diff --git a/desktop/src/features/agents/ui/AgentDialog.tsx b/desktop/src/features/agents/ui/AgentDialog.tsx index cf5deaed1..f5be3cc7e 100644 --- a/desktop/src/features/agents/ui/AgentDialog.tsx +++ b/desktop/src/features/agents/ui/AgentDialog.tsx @@ -69,7 +69,7 @@ type AgentDialogDefinitionEditProps = { input: CreatePersonaInput | UpdatePersonaInput, options: AgentDefinitionSubmitOptions, ) => Promise; - showPublishUpdatesOption?: boolean; + publishCatalogUpdatesOnSave?: boolean; }; type AgentDialogProps = diff --git a/desktop/src/features/agents/ui/AgentsView.tsx b/desktop/src/features/agents/ui/AgentsView.tsx index 30bb095f3..ce3f1ad07 100644 --- a/desktop/src/features/agents/ui/AgentsView.tsx +++ b/desktop/src/features/agents/ui/AgentsView.tsx @@ -332,7 +332,7 @@ export function AgentsView() { ) } open={personas.personaDialogState !== null} - showPublishUpdatesOption={ + publishCatalogUpdatesOnSave={ "id" in personas.personaDialogState.initialValues && personas.sharedCatalogPersonaIdSet.has( personas.personaDialogState.initialValues.id, diff --git a/desktop/tests/e2e/agents.spec.ts b/desktop/tests/e2e/agents.spec.ts index 9ff6bd296..e7a0e073b 100644 --- a/desktop/tests/e2e/agents.spec.ts +++ b/desktop/tests/e2e/agents.spec.ts @@ -1399,29 +1399,48 @@ This deliberately long fenced-code example must not establish the minimum width await page.getByLabel("Open actions for Catalog Analyst").click(); await page.getByRole("menuitem", { name: "Edit" }).click(); const editDialog = page.getByTestId("persona-dialog"); - const publishUpdatesCheckbox = editDialog.getByTestId( - "persona-dialog-publish-updates", + const catalogPublishNotice = editDialog.getByTestId( + "persona-dialog-catalog-publish-notice", ); - await expect(publishUpdatesCheckbox).toHaveCount(0); + await expect(catalogPublishNotice).toHaveCount(0); + await expect( + editDialog.getByRole("button", { name: "Save and publish" }), + ).toHaveCount(0); + await expect( + editDialog.getByRole("button", { name: "Save changes" }), + ).toBeVisible(); await editDialog .getByLabel("Agent instructions") .fill("Review the latest catalog changes."); - await expect(publishUpdatesCheckbox).toBeVisible(); - await expect(publishUpdatesCheckbox).toHaveAttribute( - "data-state", - "unchecked", + await expect(catalogPublishNotice).toHaveText( + "This agent is in the community catalog. Your changes will be published when you save.", ); - const [cancelButtonBox, publishUpdatesCheckboxBox] = await Promise.all([ - editDialog.getByRole("button", { name: "Cancel" }).boundingBox(), - publishUpdatesCheckbox.boundingBox(), - ]); - expect( - (publishUpdatesCheckboxBox?.x ?? 0) + - (publishUpdatesCheckboxBox?.width ?? 0), - ).toBeLessThan(cancelButtonBox?.x ?? 0); - await editDialog.getByRole("button", { name: "Save changes" }).click(); + await expect( + editDialog.getByRole("button", { name: "Save changes" }), + ).toHaveCount(0); + await editDialog.getByRole("button", { name: "Save and publish" }).click(); await expect(editDialog).toHaveCount(0); + await page.getByLabel("Open actions for Catalog Analyst").click(); + await page.getByRole("menuitem", { name: "Share" }).click(); + await expect(catalogAccess).toHaveText("Agent only"); + await expect(publishCatalogUpdatesButton).toHaveCount(0); + await page + .getByTestId("persona-share-dialog") + .getByRole("button", { name: "Close" }) + .click(); + + await page.evaluate((id) => { + const storageKey = "buzz-persona-catalog-published-versions-v1"; + const publishedVersions = JSON.parse( + localStorage.getItem(storageKey) ?? "{}", + ) as Record; + publishedVersions[id] = "stale"; + localStorage.setItem(storageKey, JSON.stringify(publishedVersions)); + }, personaId); + await gotoApp(page); + await page.getByTestId("open-agents-view").click(); + await page.getByLabel("Open actions for Catalog Analyst").click(); await page.getByRole("menuitem", { name: "Share" }).click(); await expect(catalogAccess).toHaveText("Agent only"); @@ -1442,17 +1461,6 @@ This deliberately long fenced-code example must not establish the minimum width .getByRole("button", { name: "Close" }) .click(); - await page.getByLabel("Open actions for Catalog Analyst").click(); - await page.getByRole("menuitem", { name: "Edit" }).click(); - await editDialog - .getByLabel("Agent instructions") - .fill("Review and publish the latest catalog changes."); - await expect(publishUpdatesCheckbox).toBeVisible(); - await publishUpdatesCheckbox.click(); - await expect(publishUpdatesCheckbox).toHaveAttribute("data-state", "checked"); - await editDialog.getByRole("button", { name: "Save changes" }).click(); - await expect(editDialog).toHaveCount(0); - await page.getByLabel("Open actions for Catalog Analyst").click(); await page.getByRole("menuitem", { name: "Share" }).click(); await expect(catalogAccess).toHaveText("Agent only");