mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
copy(desktop): the word "persona" leaves the UI (Phase 1B.4) (#1646)
Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
@@ -73,7 +73,7 @@ export function resolvePersonaRuntime(
|
||||
return {
|
||||
runtime: defaultRuntime,
|
||||
warnings: [
|
||||
`Persona is configured for runtime "${personaRuntimeId}" but it is not available. Using ${defaultRuntime.label} instead.`,
|
||||
`This agent is configured for runtime "${personaRuntimeId}" but it is not available. Using ${defaultRuntime.label} instead.`,
|
||||
],
|
||||
isOverridden: true,
|
||||
};
|
||||
@@ -82,7 +82,7 @@ export function resolvePersonaRuntime(
|
||||
return {
|
||||
runtime: null,
|
||||
warnings: [
|
||||
`Persona is configured for runtime "${personaRuntimeId}" but it is not available, and no other runtimes were found.`,
|
||||
`This agent is configured for runtime "${personaRuntimeId}" but it is not available, and no other runtimes were found.`,
|
||||
],
|
||||
isOverridden: false,
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@ function provenanceSentence(
|
||||
case "buzzExplicit":
|
||||
return "Set in Buzz";
|
||||
case "personaDefault":
|
||||
return "Inherited from persona";
|
||||
return "Inherited from template";
|
||||
case "runtimeOverride":
|
||||
return "Live override (this session only)";
|
||||
case "harnessConstraint":
|
||||
|
||||
@@ -235,7 +235,7 @@ export function AgentInstanceEditDialog({
|
||||
// The runtime id that will actually be active after submit. When inheriting,
|
||||
// resolve from the LINKED PERSONA's runtime — that is what will run once the
|
||||
// override is cleared. Deriving from agent.agentCommand here is wrong for a
|
||||
// pinned agent that just toggled "Inherit runtime from persona": the override
|
||||
// pinned agent that just toggled "Inherit runtime from template": the override
|
||||
// (e.g. a Claude pin) is still present on the record, so it would resolve to
|
||||
// the old pin instead of the persona's runtime, hiding required credentials.
|
||||
// Fall back to the agent.agentCommand dual-match (command path, then id) only
|
||||
|
||||
@@ -131,7 +131,7 @@ export function BatchImportDialog({
|
||||
<DialogContent className="max-w-2xl overflow-hidden p-0">
|
||||
<div className="flex max-h-[85vh] flex-col">
|
||||
<DialogHeader className="shrink-0 border-b border-border/60 px-6 py-5 pr-14">
|
||||
<DialogTitle>Import Personas</DialogTitle>
|
||||
<DialogTitle>Import Agents</DialogTitle>
|
||||
<DialogDescription>
|
||||
Found {personas.length} persona
|
||||
{personas.length !== 1 ? "s" : ""} in {fileName || "archive"}.
|
||||
|
||||
@@ -828,7 +828,7 @@ export function CreateAgentDialog({
|
||||
<EnvVarsEditor
|
||||
disabled={createMutation.isPending}
|
||||
fileSatisfiedKeys={fileSatisfiedEnvKeys}
|
||||
helperText="Injected at spawn. Overrides the persona's env vars on collision."
|
||||
helperText="Injected at spawn. Overrides the template's env vars on collision."
|
||||
onChange={setEnvVars}
|
||||
requiredKeys={requiredEnvKeys}
|
||||
value={envVars}
|
||||
|
||||
@@ -70,7 +70,7 @@ export function EditAgentAdvancedFields({
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-5 pt-2">
|
||||
{/* Inherit runtime from persona */}
|
||||
{/* Inherit runtime from template */}
|
||||
{linkedPersona ? (
|
||||
<div className="space-y-1.5">
|
||||
<label
|
||||
@@ -83,14 +83,14 @@ export function EditAgentAdvancedFields({
|
||||
onChange={(event) => onInheritHarnessChange(event.target.checked)}
|
||||
type="checkbox"
|
||||
/>
|
||||
Inherit runtime from persona
|
||||
Inherit runtime from template
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{inheritHarness
|
||||
? `Uses the ${linkedPersona.displayName} persona's runtime${
|
||||
? `Uses the ${linkedPersona.displayName} template's runtime${
|
||||
linkedPersona.runtime ? ` (${linkedPersona.runtime})` : ""
|
||||
}. Editing the persona and respawning propagates the new runtime.`
|
||||
: "Pins this agent to a specific runtime command, overriding the persona's runtime."}
|
||||
}. Editing the template and respawning propagates the new runtime.`
|
||||
: "Pins this agent to a specific runtime command, overriding the template's runtime."}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -366,9 +366,9 @@ export function EditAgentAdvancedFields({
|
||||
<EnvVarsEditor
|
||||
disabled={disabled}
|
||||
fileSatisfiedKeys={fileSatisfiedEnvKeys}
|
||||
helperText="Per-agent env vars. Override the persona's vars on collision."
|
||||
helperText="Per-agent env vars. Override the template's vars on collision."
|
||||
inheritedFrom={inheritedEnvVars}
|
||||
inheritedLabel="persona"
|
||||
inheritedLabel="template"
|
||||
onChange={onEnvVarsChange}
|
||||
requiredKeys={requiredEnvKeys}
|
||||
value={envVars}
|
||||
|
||||
@@ -424,7 +424,7 @@ export function EnvVarsEditor({
|
||||
|
||||
/**
|
||||
* Render a masked preview of an inherited (persona) env value so the agent
|
||||
* dialog can show "Overrides persona value •••• (last 4)" without exposing
|
||||
* dialog can show "Overrides template value •••• (last 4)" without exposing
|
||||
* the persona's actual secret to anyone viewing the agent UI. Empty values
|
||||
* render as "(empty)" so the user can still tell the persona had a value
|
||||
* set at all.
|
||||
|
||||
@@ -101,7 +101,7 @@ export function ModelPicker({
|
||||
acpConfigOption: "from ACP config",
|
||||
envVar: "from env",
|
||||
configFile: "from config file",
|
||||
personaDefault: "persona default",
|
||||
personaDefault: "template default",
|
||||
runtimeOverride: "live override",
|
||||
};
|
||||
return labels[origin] ?? null;
|
||||
|
||||
@@ -126,7 +126,7 @@ export function PersonaCatalogDetailsSheet({
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
||||
Type
|
||||
</p>
|
||||
<p className="mt-2 text-sm font-medium">Built-in persona</p>
|
||||
<p className="mt-2 text-sm font-medium">Built-in agent</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-border/70 bg-card/70 p-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
||||
|
||||
@@ -282,7 +282,7 @@ function PersonaCatalogDetail({ persona }: { persona: AgentPersona }) {
|
||||
items={[
|
||||
{
|
||||
label: "Type",
|
||||
value: persona.isBuiltIn ? "Built-in persona" : "Custom persona",
|
||||
value: persona.isBuiltIn ? "Built-in agent" : "Custom agent",
|
||||
},
|
||||
{
|
||||
label: "Preferred model",
|
||||
|
||||
@@ -28,11 +28,11 @@ export function PersonaDeleteDialog({
|
||||
<AlertDialog onOpenChange={onOpenChange} open={open}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Delete persona?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Delete agent?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{persona
|
||||
? `Delete ${persona.displayName}. Existing agents keep their copied settings, but this template will no longer be available for new deployments.`
|
||||
: "Delete this persona."}
|
||||
: "Delete this agent."}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
|
||||
@@ -131,7 +131,7 @@ export function PersonaImportUpdateDialog({
|
||||
<Dialog onOpenChange={onOpenChange} open={open}>
|
||||
<DialogContent className="flex max-h-[85vh] max-w-3xl flex-col overflow-hidden p-0">
|
||||
<DialogHeader className="shrink-0 space-y-1 border-b border-border/60 px-6 py-5 pr-14">
|
||||
<DialogTitle>Import persona</DialogTitle>
|
||||
<DialogTitle>Import agent</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-4">
|
||||
|
||||
@@ -363,7 +363,7 @@ export function TeamDialog({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<span className="text-sm font-medium">Personas</span>
|
||||
<span className="text-sm font-medium">Agents</span>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Select the personas to include in this team.
|
||||
</p>
|
||||
@@ -384,7 +384,7 @@ export function TeamDialog({
|
||||
<div
|
||||
className="max-h-60 space-y-1 overflow-y-auto rounded-lg border border-border/70 p-2"
|
||||
role="listbox"
|
||||
aria-label="Personas"
|
||||
aria-label="Agents"
|
||||
aria-multiselectable="true"
|
||||
>
|
||||
{orderedPersonas.map((persona) => {
|
||||
|
||||
@@ -148,7 +148,7 @@ export function TeamImportDialog({
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium">Personas to import</p>
|
||||
<p className="text-sm font-medium">Agents to import</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Each persona will be created, then grouped into a new team.
|
||||
</p>
|
||||
|
||||
@@ -232,7 +232,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
|
||||
agents={unknown}
|
||||
collapsed={collapsed}
|
||||
groupKey="__unknown__"
|
||||
label="Unknown Persona"
|
||||
label="Unknown Agent"
|
||||
startingAgentPubkey={startingAgentPubkey}
|
||||
onToggle={toggle}
|
||||
onOpenAgentProfile={onOpenAgentProfile}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export const personaLibraryCopy = {
|
||||
title: "My agents",
|
||||
description:
|
||||
"The personas you have chosen for this app. Use them to create teams and launch agents.",
|
||||
"The agents you have chosen for this app. Use them to create teams and launch agents.",
|
||||
chooseFromCatalog: "Choose from catalog",
|
||||
createNew: "New agent",
|
||||
customAgent: "Custom agent",
|
||||
import: "Import",
|
||||
emptyTitle: "No agents yet",
|
||||
emptyDescription:
|
||||
"Choose one from Agent Catalog, add your own persona, or import one to get started.",
|
||||
"Choose one from Agent Catalog, create your own, or import one to get started.",
|
||||
emptyImportHint:
|
||||
"Or drop a .persona.md, .persona.json, .persona.png, or .zip file here to import.",
|
||||
} as const;
|
||||
@@ -30,14 +30,14 @@ export const personaCatalogCopy = {
|
||||
availableState: "Available",
|
||||
detailSelectedTitle: "Selected for My Agents",
|
||||
detailSelectedDescription:
|
||||
"Turn this off to remove the persona from teams and agent creation in this app.",
|
||||
"Turn this off to remove the agent from teams and agent creation in this app.",
|
||||
detailAvailableTitle: "Available in Agent Catalog",
|
||||
detailAvailableDescription:
|
||||
"Turn this on to make the persona available for teams and agent creation.",
|
||||
"Turn this on to make the agent available for teams and agent creation.",
|
||||
useAction: "Add agent",
|
||||
addedAction: "Added to My Agents",
|
||||
teamEmptyState:
|
||||
"No personas in My Agents yet. Create one or choose one from Agent Catalog first.",
|
||||
"No agents in My Agents yet. Create one or choose one from Agent Catalog first.",
|
||||
} as const;
|
||||
|
||||
export function getPersonaCatalogSelectionActionCopy(isActive: boolean) {
|
||||
|
||||
@@ -119,7 +119,7 @@ export function hasMissingRequiredEnvKey(
|
||||
* The ONE exception is the inherit-TRANSITION-from-a-harness-pin: a previously
|
||||
* harness-pinned agent (e.g. Claude — `agent.agentCommandOverride != null` at
|
||||
* dialog open) has its `provider` cleared and carries no persona credential,
|
||||
* then the user checks "Inherit runtime from persona" for a provider-backed
|
||||
* then the user checks "Inherit runtime from template" for a provider-backed
|
||||
* persona (e.g. buzz-agent/Anthropic). Persisting the local (empty) provider +
|
||||
* credential-less env would save an agent that fails readiness on next start.
|
||||
* Only in that case — inheriting AND the local provider is empty AND the agent
|
||||
|
||||
@@ -232,7 +232,7 @@ export function usePersonaActions() {
|
||||
return true;
|
||||
} catch (error) {
|
||||
setPersonaErrorMessage(
|
||||
error instanceof Error ? error.message : "Failed to save persona.",
|
||||
error instanceof Error ? error.message : "Failed to save agent.",
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
@@ -248,7 +248,7 @@ export function usePersonaActions() {
|
||||
setPersonaToDelete(null);
|
||||
} catch (error) {
|
||||
setPersonaErrorMessage(
|
||||
error instanceof Error ? error.message : "Failed to delete persona.",
|
||||
error instanceof Error ? error.message : "Failed to delete agent.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -271,8 +271,8 @@ export function usePersonaActions() {
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: active
|
||||
? "Failed to select persona for My Agents."
|
||||
: "Failed to deselect persona from My Agents.",
|
||||
? "Failed to select agent for My Agents."
|
||||
: "Failed to deselect agent from My Agents.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -291,11 +291,11 @@ export function usePersonaActions() {
|
||||
setBatchImportResult(result);
|
||||
setBatchImportFileName(fileName);
|
||||
} else {
|
||||
setPersonaErrorMessage("No valid personas found in file.");
|
||||
setPersonaErrorMessage("No valid agents found in file.");
|
||||
}
|
||||
} catch (err) {
|
||||
setPersonaErrorMessage(
|
||||
err instanceof Error ? err.message : "Failed to parse persona file.",
|
||||
err instanceof Error ? err.message : "Failed to parse agent file.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -310,7 +310,7 @@ export function usePersonaActions() {
|
||||
},
|
||||
onError: (error) => {
|
||||
setPersonaErrorMessage(
|
||||
error instanceof Error ? error.message : "Failed to export persona.",
|
||||
error instanceof Error ? error.message : "Failed to export agent.",
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ export function usePersonaImportActions(
|
||||
(candidate) => candidate.id === personaId,
|
||||
);
|
||||
if (!persona) {
|
||||
const message = "Persona not found. Refresh and try again.";
|
||||
const message = "Agent not found. Refresh and try again.";
|
||||
feedback.setPersonaErrorMessage(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ export function usePersonaImportActions(
|
||||
try {
|
||||
const result = await parsePersonaFiles(fileBytes, fileName);
|
||||
if (result.personas.length === 0) {
|
||||
const message = "No valid personas found in file.";
|
||||
const message = "No valid agents found in file.";
|
||||
feedback.setPersonaErrorMessage(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export function usePersonaImportActions(
|
||||
feedback.setPersonaDialogState(null);
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : "Failed to parse persona file.";
|
||||
err instanceof Error ? err.message : "Failed to parse agent file.";
|
||||
feedback.setPersonaErrorMessage(message);
|
||||
throw err instanceof Error ? err : new Error(message);
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ export function useTeamActions(
|
||||
actions.setActionErrorMessage(null);
|
||||
setTeamDialogState({
|
||||
title: "Create team",
|
||||
description: "Group personas together for quick deployment to channels.",
|
||||
description: "Group agents together for quick deployment to channels.",
|
||||
submitLabel: "Create team",
|
||||
initialValues: {
|
||||
name: "",
|
||||
|
||||
@@ -433,7 +433,7 @@ export function AddChannelBotDialog({
|
||||
? "Loading runtimes..."
|
||||
: providers.length === 0
|
||||
? "No runtimes found"
|
||||
: (selectedRuntime?.label ?? "Use persona defaults");
|
||||
: (selectedRuntime?.label ?? "Use agent defaults");
|
||||
const addButtonLabel = createBotsMutation.isPending
|
||||
? selectedCount > 1
|
||||
? `Adding ${selectedCount}...`
|
||||
@@ -447,7 +447,7 @@ export function AddChannelBotDialog({
|
||||
<ChooserDialogContent
|
||||
className="max-w-3xl"
|
||||
data-testid="add-channel-bot-dialog"
|
||||
description="Select any combination of saved personas, or turn on Generic for a one-off custom agent."
|
||||
description="Select any combination of saved agents, or turn on Generic for a one-off custom agent."
|
||||
footer={
|
||||
<>
|
||||
<Button
|
||||
@@ -567,8 +567,8 @@ export function AddChannelBotDialog({
|
||||
</DropdownMenu>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{isOverrideActive
|
||||
? "All agents will use this runtime, overriding persona preferences."
|
||||
: "Each persona uses its preferred runtime. Choose a runtime above to override all."}
|
||||
? "All agents will use this runtime, overriding their configured preferences."
|
||||
: "Each agent uses its preferred runtime. Choose a runtime above to override all."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ export function AddChannelBotPersonasSection({
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium">Personas</div>
|
||||
<div className="text-sm font-medium">Agents</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Toggle as many as you want. Each selected persona is added as its
|
||||
own agent. Hover a persona to preview its role.
|
||||
@@ -215,7 +215,7 @@ export function AddChannelBotPersonasSection({
|
||||
</TooltipProvider>
|
||||
|
||||
{isLoading ? (
|
||||
<p className="text-xs text-muted-foreground">Loading personas...</p>
|
||||
<p className="text-xs text-muted-foreground">Loading agents...</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,7 +96,7 @@ async function ensureWelcomeGuidePersonaActive() {
|
||||
(persona) => persona.id === WELCOME_GUIDE_PERSONA_ID,
|
||||
);
|
||||
if (!guidePersona) {
|
||||
throw new Error(`${WELCOME_GUIDE_AGENT_NAME} persona not found.`);
|
||||
throw new Error(`${WELCOME_GUIDE_AGENT_NAME} agent not found.`);
|
||||
}
|
||||
if (!guidePersona.isActive) {
|
||||
await setPersonaActive(WELCOME_GUIDE_PERSONA_ID, true);
|
||||
|
||||
@@ -1445,7 +1445,7 @@ function buildMockConfigSurface(pubkey: string): {
|
||||
|
||||
// Mixed-provenance showcase — top-level rows carry different origins so the
|
||||
// panel witnesses distinct provenance labels in one frame: "Set in Buzz",
|
||||
// "Inherited from persona", "From config file (...)" and
|
||||
// "Inherited from template", "From config file (...)" and
|
||||
// "From environment variable (...)".
|
||||
const multiOriginSurface = {
|
||||
runtimeId: "goose",
|
||||
|
||||
@@ -336,7 +336,7 @@ test("catalog detail pane shows the full persona details", async ({ page }) => {
|
||||
"You are Fizz.",
|
||||
);
|
||||
await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText(
|
||||
"Built-in persona",
|
||||
"Built-in agent",
|
||||
);
|
||||
await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText(
|
||||
"Preferred model",
|
||||
@@ -409,7 +409,7 @@ test("custom personas can be shown in the agent catalog", async ({ page }) => {
|
||||
).toContainText("Analyst");
|
||||
await selectCatalogPersona(page, analystPersonaId);
|
||||
await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText(
|
||||
"Custom persona",
|
||||
"Custom agent",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId(`persona-catalog-use-agent-target-${analystPersonaId}`),
|
||||
|
||||
@@ -210,7 +210,7 @@ test.describe("config bridge screenshots", () => {
|
||||
|
||||
// Multiple distinct provenance origins visible at once.
|
||||
await expect(panel.getByText("Set in Buzz").first()).toBeVisible();
|
||||
await expect(panel.getByText("Inherited from persona")).toBeVisible();
|
||||
await expect(panel.getByText("Inherited from template")).toBeVisible();
|
||||
await expect(
|
||||
panel.getByText("From environment variable (GOOSE_MODE)"),
|
||||
).toBeVisible();
|
||||
|
||||
Reference in New Issue
Block a user