diff --git a/src/features/agents/lib/agentBuilderIdentity.ts b/src/features/agents/lib/agentBuilderIdentity.ts index 5edff2ef2..f96ea62cd 100644 --- a/src/features/agents/lib/agentBuilderIdentity.ts +++ b/src/features/agents/lib/agentBuilderIdentity.ts @@ -45,6 +45,17 @@ export function isPlaceholderAgentName(name: string): boolean { ); } +// Re-exported here so callers dealing with agent/draft identity (name, +// description, body placeholders) have one place to import from, alongside +// isPlaceholderAgentName above. The check itself lives in +// @/shared/api/agents, which needs it for create/update/export — this module +// only imports a *type* from there (erased at build time), so re-exporting +// its values here doesn't create a real circular dependency. +export { + hasRealAgentDescription, + isPlaceholderAgentDescription, +} from "@/shared/api/agents"; + export function isEmptyPlaceholderDraft(source: AgentSourceEntry): boolean { const builderSessionId = typeof source.properties?.builderSessionId === "string" diff --git a/src/features/agents/lib/personaPresentation.test.ts b/src/features/agents/lib/personaPresentation.test.ts index 70c7f645d..22707b9f5 100644 --- a/src/features/agents/lib/personaPresentation.test.ts +++ b/src/features/agents/lib/personaPresentation.test.ts @@ -4,6 +4,7 @@ import { canDeletePersona, canEditPersona, getPersonaSource, + getRealPersonaDescription, isPersonaReadOnly, } from "./personaPresentation"; @@ -34,4 +35,38 @@ describe("personaPresentation", () => { expect(getPersonaSource(persona({ writable: true }))).toBe("file"); expect(getPersonaSource(persona({ writable: false }))).toBe("builtin"); }); + + it("returns a real, user-authored description as-is", () => { + expect( + getRealPersonaDescription( + persona({ sourceDescription: "Reviews your code and catches bugs." }), + ), + ).toBe("Reviews your code and catches bugs."); + }); + + it("treats the legacy 'Agent' placeholder as no description", () => { + expect( + getRealPersonaDescription(persona({ sourceDescription: "Agent" })), + ).toBeUndefined(); + // Case-insensitive and trims whitespace, since the placeholder could + // come back from the API in either form. + expect( + getRealPersonaDescription(persona({ sourceDescription: " AGENT " })), + ).toBeUndefined(); + }); + + it("treats the builder-draft 'Draft' placeholder as no description", () => { + expect( + getRealPersonaDescription(persona({ sourceDescription: "Draft" })), + ).toBeUndefined(); + }); + + it("treats a missing or empty description as no description", () => { + expect( + getRealPersonaDescription(persona({ sourceDescription: undefined })), + ).toBeUndefined(); + expect( + getRealPersonaDescription(persona({ sourceDescription: " " })), + ).toBeUndefined(); + }); }); diff --git a/src/features/agents/lib/personaPresentation.ts b/src/features/agents/lib/personaPresentation.ts index 1ae479aea..03e64d5d8 100644 --- a/src/features/agents/lib/personaPresentation.ts +++ b/src/features/agents/lib/personaPresentation.ts @@ -1,7 +1,16 @@ import type { Persona } from "@/shared/types/agents"; +import { hasRealAgentDescription } from "@/features/agents/lib/agentBuilderIdentity"; export type PersonaSource = "builtin" | "file"; +export function getRealPersonaDescription( + persona: Persona, +): string | undefined { + return hasRealAgentDescription(persona.sourceDescription) + ? persona.sourceDescription?.trim() + : undefined; +} + type ProviderLabel = { id: string; label: string; diff --git a/src/features/agents/ui/AgentBuilderRail.tsx b/src/features/agents/ui/AgentBuilderRail.tsx index f97236936..47f17c0e4 100644 --- a/src/features/agents/ui/AgentBuilderRail.tsx +++ b/src/features/agents/ui/AgentBuilderRail.tsx @@ -38,6 +38,7 @@ import { fileStem, isPlaceholderAgentName, PLACEHOLDER_AGENT_BODY, + PLACEHOLDER_AGENT_DESCRIPTION, promoteDraft, } from "@/features/agents/lib/agentBuilderSession"; import { useExperiment } from "@/features/experiments/experimentPreferences"; @@ -302,6 +303,10 @@ export function AgentBuilderRail({ : null; const nameFieldValue = data && !isPlaceholderAgentName(data.name) ? data.name : ""; + const descriptionFieldValue = + data && data.description !== PLACEHOLDER_AGENT_DESCRIPTION + ? data.description + : ""; const contentFieldValue = data?.content ?? ""; const isPlaceholderContent = contentFieldValue === PLACEHOLDER_AGENT_BODY; const instructionsFieldValue = isPlaceholderContent ? "" : contentFieldValue; @@ -712,6 +717,19 @@ export function AgentBuilderRail({ /> + + ; + ].filter(Boolean) as Array<{ + label: string; + value: string; + multiline?: boolean; + }>; if (previousPersonaAvatarValue !== personaAvatarValue) { setPreviousPersonaAvatarValue(personaAvatarValue); @@ -274,7 +287,7 @@ export function AgentDetailPage({ className={AVATAR_CUSTOMIZE_LABEL_CLASS} aria-hidden="true" > - {t("editor.changeAvatar")} + {t("builderRail.changeAvatar")} ) : null} diff --git a/src/features/agents/ui/AgentIdentityRail.tsx b/src/features/agents/ui/AgentIdentityRail.tsx index ad3d71e8c..84ce205c8 100644 --- a/src/features/agents/ui/AgentIdentityRail.tsx +++ b/src/features/agents/ui/AgentIdentityRail.tsx @@ -4,7 +4,10 @@ import { cn } from "@/shared/lib/cn"; interface AgentIdentityMetadataItem { label: string; value: string; + /** Breaks anywhere, for unbroken strings like file paths. */ wrap?: boolean; + /** Wraps at word boundaries, for real prose like a description. */ + multiline?: boolean; } interface AgentIdentityRailProps { @@ -62,7 +65,11 @@ export function AgentIdentityRail({
{item.value} diff --git a/src/features/agents/ui/PersonaCard.tsx b/src/features/agents/ui/PersonaCard.tsx index e63bd85d5..f59d927e5 100644 --- a/src/features/agents/ui/PersonaCard.tsx +++ b/src/features/agents/ui/PersonaCard.tsx @@ -24,6 +24,7 @@ import type { Persona } from "@/shared/types/agents"; import { canDeletePersona, canEditPersona, + getRealPersonaDescription, } from "@/features/agents/lib/personaPresentation"; import { resolveAgentIcon } from "@/features/agents/lib/resolveAgentIcon"; import { getAgentAvatarTransitionName } from "@/features/agents/lib/agentViewTransitions"; @@ -254,7 +255,7 @@ export const PersonaCard = memo(function PersonaCard({

- {persona.systemPrompt} + {getRealPersonaDescription(persona) ?? persona.systemPrompt}

diff --git a/src/features/agents/ui/__tests__/AgentBuilderRail.test.tsx b/src/features/agents/ui/__tests__/AgentBuilderRail.test.tsx index bc70be2fa..6af284ef4 100644 --- a/src/features/agents/ui/__tests__/AgentBuilderRail.test.tsx +++ b/src/features/agents/ui/__tests__/AgentBuilderRail.test.tsx @@ -22,6 +22,7 @@ vi.mock("@/features/agents/lib/agentBuilderSession", () => ({ name === "Untitled agent" || name.startsWith("Untitled agent "), PLACEHOLDER_AGENT_NAME: "Untitled agent", PLACEHOLDER_AGENT_BODY: "Draft in progress.", + PLACEHOLDER_AGENT_DESCRIPTION: "Draft", })); vi.mock("@/features/agents/hooks/useAvatarLibrary", () => ({ @@ -211,6 +212,35 @@ describe("AgentBuilderRail", () => { expect(update).toHaveBeenCalledWith({ content: "Be snarky." }); }); + it("calls update() when the description field changes", () => { + const { update } = mockHook(); + renderWithProviders( + , + ); + fireEvent.change(screen.getByLabelText(/description/i), { + target: { value: "Catches bugs before you ship them." }, + }); + expect(update).toHaveBeenCalledWith({ + description: "Catches bugs before you ship them.", + }); + }); + + it("treats the placeholder draft description as empty in the field", () => { + mockHook({ data: { ...baseSource, description: "Draft" } }); + renderWithProviders( + , + ); + expect(screen.getByLabelText(/description/i)).toHaveValue(""); + }); + it("renders the placeholder draft body as muted placeholder text", () => { mockHook(); renderWithProviders( diff --git a/src/features/agents/ui/__tests__/AgentsView.entry.test.tsx b/src/features/agents/ui/__tests__/AgentsView.entry.test.tsx index c68326e80..65ca2f5ae 100644 --- a/src/features/agents/ui/__tests__/AgentsView.entry.test.tsx +++ b/src/features/agents/ui/__tests__/AgentsView.entry.test.tsx @@ -73,6 +73,14 @@ vi.mock("@/shared/api/agents", () => ({ readAgentSourceFile: vi.fn().mockResolvedValue(mockDraftSource), updatePersonaSource: vi.fn().mockResolvedValue(mockDraftSource), deletePersonaSource: vi.fn().mockResolvedValue(undefined), + isPlaceholderAgentDescription: (description: string | undefined | null) => { + const trimmed = description?.trim().toLowerCase(); + return !trimmed || trimmed === "agent" || trimmed === "draft"; + }, + hasRealAgentDescription: (description: string | undefined | null) => { + const trimmed = description?.trim().toLowerCase(); + return Boolean(trimmed) && trimmed !== "agent" && trimmed !== "draft"; + }, })); vi.mock("@/shared/api/system", () => ({ @@ -373,6 +381,29 @@ describe("AgentsView entry points", () => { expect(onActivePersonaIdChange).toHaveBeenCalledWith(null, undefined); }); + it("shows the agent's description on the detail page, next to provider and model", () => { + useAgentStore.setState({ + personas: [ + { ...persona, sourceDescription: "Reviews your code carefully." }, + ], + }); + + render(); + + expect(screen.getByText("view.description")).toBeInTheDocument(); + expect( + screen.getByText("Reviews your code carefully."), + ).toBeInTheDocument(); + }); + + it("shows no description row on the detail page when there's no real description", () => { + useAgentStore.setState({ personas: [persona] }); + + render(); + + expect(screen.queryByText("view.description")).not.toBeInTheDocument(); + }); + it("shows and activates the avatar customization affordance", async () => { useAgentStore.setState({ personas: [persona] }); const user = userEvent.setup(); @@ -382,7 +413,7 @@ describe("AgentsView entry points", () => { const customizeAvatar = screen.getByRole("button", { name: "editor.customizeAvatar", }); - expect(screen.getByText("editor.changeAvatar")).toBeInTheDocument(); + expect(screen.getByText("builderRail.changeAvatar")).toBeInTheDocument(); customizeAvatar.focus(); expect(customizeAvatar).toHaveFocus(); await user.keyboard("{Enter}"); diff --git a/src/features/agents/ui/__tests__/PersonaCard.test.tsx b/src/features/agents/ui/__tests__/PersonaCard.test.tsx index 23650bcfb..475c100db 100644 --- a/src/features/agents/ui/__tests__/PersonaCard.test.tsx +++ b/src/features/agents/ui/__tests__/PersonaCard.test.tsx @@ -146,10 +146,42 @@ describe("PersonaCard", () => { expect(screen.queryByText(/claude-sonnet/i)).not.toBeInTheDocument(); }); - it("shows system prompt preview", () => { + it("shows the agent's description, not its instructions", () => { render( , + ); + expect( + screen.getByText("Reviews your code and catches bugs."), + ).toBeInTheDocument(); + expect( + screen.queryByText("You are a coding assistant."), + ).not.toBeInTheDocument(); + }); + + it("falls back to instructions when there's no real description", () => { + render( + , + ); + expect(screen.getByText("You are a coding assistant.")).toBeInTheDocument(); + }); + + it("falls back to instructions when the description is a placeholder", () => { + render( + , ); expect(screen.getByText("You are a coding assistant.")).toBeInTheDocument(); diff --git a/src/features/agents/ui/share-card/agentShareCard.ts b/src/features/agents/ui/share-card/agentShareCard.ts index d7b5939c7..ef8f43943 100644 --- a/src/features/agents/ui/share-card/agentShareCard.ts +++ b/src/features/agents/ui/share-card/agentShareCard.ts @@ -2,6 +2,7 @@ import cardFoil from "@/features/agents/assets/share-card/card-foil.png"; import berdCardLogo from "@/features/agents/assets/share-card/berd-card-logo.svg"; import type { ResolvedAvatarMedia } from "@/shared/avatars/catalog"; import type { Persona } from "@/shared/types/agents"; +import { getRealPersonaDescription } from "@/features/agents/lib/personaPresentation"; import { fallbackAgentCardColor, sampleAgentAvatarColor, @@ -24,13 +25,7 @@ export function getAgentShareCardBase(_personaId: string): string { } export function getAgentShareDescription(persona: Persona): string { - const sourceDescription = persona.sourceDescription?.trim(); - const candidate = - sourceDescription && sourceDescription.toLowerCase() !== "agent" - ? sourceDescription - : persona.systemPrompt.trim(); - - return candidate; + return getRealPersonaDescription(persona) ?? persona.systemPrompt.trim(); } export function getAgentShareFilename(name: string): string { diff --git a/src/shared/api/__tests__/agents.test.ts b/src/shared/api/__tests__/agents.test.ts index af1dad3d3..59b3c2643 100644 --- a/src/shared/api/__tests__/agents.test.ts +++ b/src/shared/api/__tests__/agents.test.ts @@ -308,6 +308,46 @@ describe("agents API", () => { expect(result.avatar).toBe("https://example.test/scout.png"); }); + it("uses a real description when creating a persona", async () => { + mockGooseSourcesCreate.mockResolvedValue({ source: agentSource }); + + const { createPersona } = await import("../agents"); + await createPersona({ + displayName: "Scout", + systemPrompt: "Research carefully.", + description: "Finds the source you actually need.", + }); + + expect(mockGooseSourcesCreate).toHaveBeenCalledWith( + expect.objectContaining({ + description: "Finds the source you actually need.", + }), + ); + }); + + it.each([ + undefined, + "", + " ", + "Agent", + "agent", + "Draft", + " draft ", + ])("falls back to the placeholder description when creating with %j", async (description) => { + mockGooseSourcesCreate.mockResolvedValue({ source: agentSource }); + + const { createPersona } = await import("../agents"); + await createPersona({ + displayName: "Scout", + systemPrompt: "Research carefully.", + description, + }); + + expect(mockGooseSourcesCreate).toHaveBeenCalledWith( + expect.objectContaining({ description: "Agent" }), + ); + }); + it("does not store unsupported avatar values on create", async () => { mockGooseSourcesCreate.mockResolvedValue({ source: agentSource }); @@ -402,6 +442,81 @@ describe("agents API", () => { }); }); + it("uses a new real description when updating a persona", async () => { + mockGooseSourcesUpdate.mockResolvedValue({ source: agentSource }); + + const { updatePersona } = await import("../agents"); + await updatePersona(loadedPersona, { + description: "Finds the source you actually need.", + }); + + expect(mockGooseSourcesUpdate).toHaveBeenCalledWith( + expect.objectContaining({ + description: "Finds the source you actually need.", + }), + ); + }); + + it("keeps the persona's existing real description when the update omits it", async () => { + mockGooseSourcesUpdate.mockResolvedValue({ source: agentSource }); + + const { updatePersona } = await import("../agents"); + await updatePersona( + { + ...loadedPersona, + sourceDescription: "Finds the source you actually need.", + }, + { systemPrompt: "Updated prompt." }, + ); + + expect(mockGooseSourcesUpdate).toHaveBeenCalledWith( + expect.objectContaining({ + description: "Finds the source you actually need.", + }), + ); + }); + + it.each([ + "", + " ", + "Agent", + "agent", + "Draft", + " draft ", + ])("falls back to the placeholder description when the update explicitly clears it with %j", async (description) => { + mockGooseSourcesUpdate.mockResolvedValue({ source: agentSource }); + + const { updatePersona } = await import("../agents"); + // Same behavior as create: explicitly passing a cleared/placeholder + // value is a real edit, not "leave it alone" — it does not fall back + // to whatever the persona already had. + await updatePersona( + { + ...loadedPersona, + sourceDescription: "Finds the source you actually need.", + }, + { description }, + ); + + expect(mockGooseSourcesUpdate).toHaveBeenCalledWith( + expect.objectContaining({ description: "Agent" }), + ); + }); + + it("falls back to the placeholder description when the persona never had a real one and the update omits it", async () => { + mockGooseSourcesUpdate.mockResolvedValue({ source: agentSource }); + + const { updatePersona } = await import("../agents"); + await updatePersona( + { ...loadedPersona, sourceDescription: "Draft" }, + { systemPrompt: "Updated prompt." }, + ); + + expect(mockGooseSourcesUpdate).toHaveBeenCalledWith( + expect.objectContaining({ description: "Agent" }), + ); + }); + it("clears modeled properties while preserving unknown source properties", async () => { mockGooseSourcesUpdate.mockResolvedValue({ source: { @@ -439,7 +554,11 @@ describe("agents API", () => { type: "agent", path: agentSource.path, name: "Scout", - description: "", + // The persona's existing sourceDescription is an empty string here, + // which was never a real, user-authored description to begin with, + // so it falls back to the placeholder rather than being sent through + // verbatim. + description: "Agent", content: "Research carefully.", properties: { provider: null, @@ -702,8 +821,12 @@ describe("agents API", () => { expect(mockGooseSourcesList).toHaveBeenCalledWith({ type: "agent" }); expect(mockGooseSourcesExport).not.toHaveBeenCalled(); expect(result).toEqual({ + // agentSource's description ("Agent") is the API-required placeholder + // used when there's no real, user-authored description, not something + // a user wrote on purpose, so export substitutes a real fallback + // rather than writing "Agent" into the exported file's frontmatter. contents: - "---\nname: scout\ndisplay_name: Scout\ndescription: Agent\nmodel: openai:gpt-4.1\navatar: https://example.test/scout.png\n---\n\nResearch carefully.\n", + "---\nname: scout\ndisplay_name: Scout\ndescription: Imported Goose agent\nmodel: openai:gpt-4.1\navatar: https://example.test/scout.png\n---\n\nResearch carefully.\n", filename: "scout.persona.md", mimeType: "text/markdown", }); @@ -799,8 +922,9 @@ describe("agents API", () => { ); expect(result).toEqual({ + // Same placeholder-description substitution as above. contents: - '---\nname: scout\ndisplay_name: Scout\ndescription: Agent\nmodel: openai:gpt-4.1\navatar: https://example.test/scout.png\nsubscribe:\n - "#agents"\ntags:\n - research\n - support\ntools:\n web: true\n---\n\nResearch carefully.\n', + '---\nname: scout\ndisplay_name: Scout\ndescription: Imported Goose agent\nmodel: openai:gpt-4.1\navatar: https://example.test/scout.png\nsubscribe:\n - "#agents"\ntags:\n - research\n - support\ntools:\n web: true\n---\n\nResearch carefully.\n', filename: "scout.persona.md", mimeType: "text/markdown", }); diff --git a/src/shared/api/agents.ts b/src/shared/api/agents.ts index 62efcde1b..590b29534 100644 --- a/src/shared/api/agents.ts +++ b/src/shared/api/agents.ts @@ -17,6 +17,17 @@ import { const AGENT_SOURCE_TYPE = "agent" as const; const AGENT_DESCRIPTION = "Agent"; +// Keep this string literal in sync with PLACEHOLDER_AGENT_DESCRIPTION in +// @/features/agents/lib/agentBuilderIdentity.ts. Defined locally, not +// imported, since that module re-exports the functions below and importing +// its value in the other direction would create a real circular import +// (unlike the type-only import that module has on this one). +const DRAFT_AGENT_DESCRIPTION = "Draft"; +const PLACEHOLDER_AGENT_DESCRIPTIONS = new Set( + [AGENT_DESCRIPTION, DRAFT_AGENT_DESCRIPTION].map((value) => + value.toLowerCase(), + ), +); const PERSONA_MD_EXTENSION = ".persona.md"; const PORTABLE_SPROUT_FRONTMATTER_KEYS = new Set([ "name", @@ -26,6 +37,26 @@ const PORTABLE_SPROUT_FRONTMATTER_KEYS = new Set([ "avatar", ]); +// The API layer requires a non-empty description on every source, so a +// persona with no real, user-authored description still gets a placeholder +// string under the hood: "Agent" (the old default, before a description +// field existed in the create/edit form) or "Draft" (a builder draft in +// progress). Neither was written by the user on purpose, so treat both as +// "no real description" everywhere a persona's description is read back and +// shown, exported, or used as a fallback for a newer write. +export function isPlaceholderAgentDescription( + description: string | undefined | null, +): boolean { + const trimmed = description?.trim().toLowerCase(); + return !trimmed || PLACEHOLDER_AGENT_DESCRIPTIONS.has(trimmed); +} + +export function hasRealAgentDescription( + description: string | undefined | null, +): boolean { + return !isPlaceholderAgentDescription(description); +} + export type AgentSourceProperties = { [key: string]: unknown; provider?: string | null; @@ -241,10 +272,9 @@ function sproutFrontmatterFromProperties( function serializePersonaMarkdown(source: AgentSourceEntry): ExportResult { const properties = source.properties; const name = personaExportName(source); - const description = - source.description.trim().length > 0 - ? source.description - : "Imported Goose agent"; + const description = hasRealAgentDescription(source.description) + ? source.description + : "Imported Goose agent"; const frontmatter: Record = { name, display_name: source.name, @@ -798,7 +828,9 @@ export async function createPersona( const response = await client.goose.GooseUnstableSourcesCreate({ type: AGENT_SOURCE_TYPE, name: request.displayName, - description: AGENT_DESCRIPTION, + description: hasRealAgentDescription(request.description) + ? (request.description as string).trim() + : AGENT_DESCRIPTION, content: request.systemPrompt, target: { scope: "global" }, properties: personaProperties(request), @@ -824,7 +856,19 @@ export async function updatePersona( request: UpdatePersonaRequest, ): Promise { const client = await getClient(); - const description = persona.sourceDescription ?? AGENT_DESCRIPTION; + // A description explicitly passed in this request wins, even if it's an + // empty string (clearing the field is a real, intentional edit — same + // as create, an empty/placeholder value falls back rather than being + // written through literally). Otherwise, keep whatever the persona + // already had, falling back only if that was never real to begin with. + const description = + request.description !== undefined + ? hasRealAgentDescription(request.description) + ? request.description.trim() + : AGENT_DESCRIPTION + : hasRealAgentDescription(persona.sourceDescription) + ? (persona.sourceDescription as string).trim() + : AGENT_DESCRIPTION; const response = await client.goose.GooseUnstableSourcesUpdate({ type: AGENT_SOURCE_TYPE, diff --git a/src/shared/i18n/locales/en/agents.json b/src/shared/i18n/locales/en/agents.json index d0a3d329f..41a09635e 100644 --- a/src/shared/i18n/locales/en/agents.json +++ b/src/shared/i18n/locales/en/agents.json @@ -22,6 +22,8 @@ "invalidFrontmatterTitle": "Invalid frontmatter", "invalidFrontmatterBody": "Fix in chat, or open the source file in an editor.", "openFailed": "Couldn't open the agent editor.", + "descriptionLabel": "Description", + "descriptionPlaceholder": "A short line about what this agent does", "instructionsLabel": "Agent instructions", "instructionsPlaceholder": "Describe the agent's goal and instructions", "selectedAvatar": "Selected avatar", @@ -231,6 +233,7 @@ "title": "Agents", "backToAgents": "Back to agents", "created": "Created", + "description": "Description", "more": "More", "source": "Source", "updated": "Updated" diff --git a/src/shared/i18n/locales/es/agents.json b/src/shared/i18n/locales/es/agents.json index 1f5155aec..a955700a9 100644 --- a/src/shared/i18n/locales/es/agents.json +++ b/src/shared/i18n/locales/es/agents.json @@ -22,6 +22,8 @@ "invalidFrontmatterTitle": "Frontmatter inválido", "invalidFrontmatterBody": "Corrígelo desde el chat o abre el archivo en un editor.", "openFailed": "No se pudo abrir el editor de agente.", + "descriptionLabel": "Descripción", + "descriptionPlaceholder": "Una línea breve sobre lo que hace este agente", "instructionsLabel": "Instrucciones del agente", "instructionsPlaceholder": "Describe el objetivo e instrucciones del agente", "selectedAvatar": "Avatar seleccionado", @@ -231,6 +233,7 @@ "title": "Agentes", "backToAgents": "Volver a agentes", "created": "Creado", + "description": "Descripción", "more": "Más", "source": "Origen", "updated": "Actualizado" diff --git a/src/shared/types/agents.ts b/src/shared/types/agents.ts index 655823e55..70af47157 100644 --- a/src/shared/types/agents.ts +++ b/src/shared/types/agents.ts @@ -26,6 +26,7 @@ export interface Persona { export interface CreatePersonaRequest { displayName: string; + description?: string; avatar?: Avatar | null; systemPrompt: string; provider?: ProviderType; @@ -35,6 +36,7 @@ export interface CreatePersonaRequest { export interface UpdatePersonaRequest { displayName?: string; + description?: string; avatar?: Avatar | null; systemPrompt?: string; provider?: ProviderType | null;