fix(desktop): allow saving personas with an empty system prompt (#1276)

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
Tyler
2026-06-25 10:05:41 -04:00
committed by GitHub
co-authored by npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d Tyler Longwell
parent 5306735986
commit 996a3f89d5
4 changed files with 51 additions and 8 deletions
+3 -3
View File
@@ -167,13 +167,13 @@ pub fn create_persona(
state: State<'_, AppState>,
) -> Result<PersonaRecord, String> {
let display_name = trim_required(&input.display_name, "Display name")?;
let system_prompt = trim_required(&input.system_prompt, "System prompt")?;
// System prompt optional: core memory is auto-injected. Empty is valid.
let system_prompt = input.system_prompt.trim().to_string();
let avatar_url = trim_optional(input.avatar_url);
let runtime = trim_optional(input.runtime);
let model = trim_optional(input.model);
let provider = trim_optional(input.provider);
let now = now_iso();
let _store_guard = state
.managed_agents_store_lock
.lock()
@@ -217,7 +217,7 @@ pub fn update_persona(
state: State<'_, AppState>,
) -> Result<PersonaRecord, String> {
let display_name = trim_required(&input.display_name, "Display name")?;
let system_prompt = trim_required(&input.system_prompt, "System prompt")?;
let system_prompt = input.system_prompt.trim().to_string();
let avatar_url = trim_optional(input.avatar_url);
let runtime = trim_optional(input.runtime);
let model = trim_optional(input.model);
@@ -25,6 +25,7 @@ import {
getImportErrorLabel,
IMPORT_ERROR_VISIBILITY_MS,
} from "./personaDialogImportState";
import { canSubmitPersonaDialog } from "./personaDialogState";
type PersonaDialogProps = {
open: boolean;
@@ -519,11 +520,7 @@ export function PersonaDialog({
Cancel
</Button>
<Button
disabled={
displayName.trim().length === 0 ||
systemPrompt.trim().length === 0 ||
isPending
}
disabled={!canSubmitPersonaDialog({ displayName, isPending })}
onClick={() => void handleSubmit()}
size="sm"
type="button"
@@ -2,12 +2,44 @@ import assert from "node:assert/strict";
import test from "node:test";
import {
canSubmitPersonaDialog,
createPersonaDialogState,
duplicatePersonaDialogState,
editPersonaDialogState,
importPersonaDialogState,
} from "./personaDialogState.ts";
test("canSubmitPersonaDialog requires a display name but not a system prompt", () => {
// Empty system prompt is allowed: core memory is auto-injected, so the
// persona prompt is optional. Only the display name gates submission.
assert.equal(
canSubmitPersonaDialog({ displayName: "Coder", isPending: false }),
true,
);
assert.equal(
canSubmitPersonaDialog({ displayName: " Coder ", isPending: false }),
true,
);
});
test("canSubmitPersonaDialog blocks an empty or whitespace display name", () => {
assert.equal(
canSubmitPersonaDialog({ displayName: "", isPending: false }),
false,
);
assert.equal(
canSubmitPersonaDialog({ displayName: " ", isPending: false }),
false,
);
});
test("canSubmitPersonaDialog blocks while a save is pending", () => {
assert.equal(
canSubmitPersonaDialog({ displayName: "Coder", isPending: true }),
false,
);
});
test("createPersonaDialogState returns a fresh empty draft", () => {
const first = createPersonaDialogState();
const second = createPersonaDialogState();
@@ -14,6 +14,20 @@ export type PersonaDialogState = {
type ParsedPersonaDraft = ParsePersonaFilesResult["personas"][number];
/**
* Whether the persona dialog's save action should be enabled.
*
* A display name is the only required field. The system prompt is optional:
* core memory is auto-injected at runtime, so a persona need not carry its
* own prompt. `isPending` blocks double-submits while a save is in flight.
*/
export function canSubmitPersonaDialog(args: {
displayName: string;
isPending: boolean;
}): boolean {
return args.displayName.trim().length > 0 && !args.isPending;
}
export function createPersonaDialogState(): PersonaDialogState {
return {
title: "Create persona",