Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/commands/personas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ mod tests {
let md = br#"---
name: fizz
display_name: Fizz
avatar: app-avatar:pollies-12
avatar: app-avatar:persona-12
runtime: goose
---
You are Fizz.
Expand All @@ -388,7 +388,7 @@ You are Fizz.
assert_eq!(result.personas[0].display_name, "Fizz");
assert_eq!(
result.personas[0].avatar_ref.as_deref(),
Some("app-avatar:pollies-12")
Some("app-avatar:persona-12")
);
assert_eq!(result.personas[0].source_file, "fizz.md");
}
Expand Down
12 changes: 6 additions & 6 deletions desktop/src-tauri/src/managed_agents/persona_card_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ fn parse_md_persona_preserves_app_avatar_ref() {
name: goosey
display_name: Goosey
description: Goose internal agent.
avatar: app-avatar:gloopies-19
avatar: app-avatar:persona-19
model: anthropic:claude-sonnet-4
runtime: goose
---
Expand All @@ -418,7 +418,7 @@ You are Goosey.
let result = parse_md_persona(md).unwrap();
assert_eq!(result.display_name, "Goosey");
assert_eq!(result.avatar_data_url, None);
assert_eq!(result.avatar_ref.as_deref(), Some("app-avatar:gloopies-19"));
assert_eq!(result.avatar_ref.as_deref(), Some("app-avatar:persona-19"));
assert_eq!(result.model.as_deref(), Some("claude-sonnet-4"));
assert_eq!(result.provider.as_deref(), Some("anthropic"));
assert_eq!(result.runtime.as_deref(), Some("goose"));
Expand Down Expand Up @@ -446,15 +446,15 @@ fn parse_md_persona_accepts_goose_internal_frontmatter() {
let md = br#"---
name: block.md
description: Opinionated guide to Block's intelligence operating model.
avatar: app-avatar:gloopies-19
avatar: app-avatar:persona-19
metadata:
gooseInternalBundled: true
---
You are block.md.
"#;
let result = parse_md_persona(md).unwrap();
assert_eq!(result.display_name, "block.md");
assert_eq!(result.avatar_ref.as_deref(), Some("app-avatar:gloopies-19"));
assert_eq!(result.avatar_ref.as_deref(), Some("app-avatar:persona-19"));
assert_eq!(result.system_prompt, "You are block.md.\n");
}

Expand Down Expand Up @@ -495,7 +495,7 @@ fn parse_zip_with_plain_md_persona_preserves_avatar_ref() {
name: fizz
display_name: Fizz
description: Engineering agent.
avatar: app-avatar:pollies-12
avatar: app-avatar:persona-12
runtime: goose
model: anthropic:claude-sonnet-4
---
Expand All @@ -508,7 +508,7 @@ You are Fizz.
assert_eq!(result.personas[0].display_name, "Fizz");
assert_eq!(
result.personas[0].avatar_ref.as_deref(),
Some("app-avatar:pollies-12")
Some("app-avatar:persona-12")
);
assert_eq!(result.personas[0].source_file, "fizz.md");
}
Expand Down
37 changes: 9 additions & 28 deletions desktop/src/shared/avatars/gooseAppAvatarRefs.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,22 @@ import {

test("toGooseAppAvatarRef canonicalizes app-avatar refs", () => {
assert.equal(
toGooseAppAvatarRef("app-avatar:gloopies-19"),
"app-avatar:gloopies-19",
toGooseAppAvatarRef("app-avatar:persona-19"),
"app-avatar:persona-19",
);
});

test("toGooseAppAvatarRef ignores Goose-looking paths by default", () => {
assert.equal(toGooseAppAvatarRef("./avatars/pollies_2.png"), null);
});

test("toGooseAppAvatarRef detects Goose avatar ids in paths during import", () => {
assert.equal(
toGooseAppAvatarRef("./avatars/pollies_2.png", {
allowFilenameFallback: true,
}),
"app-avatar:pollies-2",
);
test("toGooseAppAvatarRef ignores filesystem-looking paths", () => {
assert.equal(toGooseAppAvatarRef("./avatars/persona_2.png"), null);
});

test("resolveImportedPersonaAvatarUrl prefers app-avatar refs over data URLs", () => {
assert.equal(
resolveImportedPersonaAvatarUrl({
avatarDataUrl: "https://example.com/avatar.png",
avatarRef: "app-avatar:fuzzies-1",
avatarRef: "app-avatar:persona-1",
}),
"app-avatar:fuzzies-1",
"app-avatar:persona-1",
);
});

Expand All @@ -46,13 +37,13 @@ test("resolveImportedPersonaAvatarUrl preserves ordinary image URLs", () => {
);
});

test("resolveImportedPersonaAvatarUrl does not rewrite Goose-looking remote URLs", () => {
test("resolveImportedPersonaAvatarUrl does not rewrite remote image URLs", () => {
assert.equal(
resolveImportedPersonaAvatarUrl({
avatarDataUrl: "https://cdn.example.com/avatars/pollies_2.png",
avatarDataUrl: "https://cdn.example.com/avatars/persona_2.png",
avatarRef: null,
}),
"https://cdn.example.com/avatars/pollies_2.png",
"https://cdn.example.com/avatars/persona_2.png",
);
});

Expand All @@ -65,13 +56,3 @@ test("resolveImportedPersonaAvatarUrl preserves URL avatar refs", () => {
"https://example.com/persona-avatar.png",
);
});

test("resolveImportedPersonaAvatarUrl preserves Goose-looking URL avatar refs", () => {
assert.equal(
resolveImportedPersonaAvatarUrl({
avatarDataUrl: null,
avatarRef: " https://cdn.example.com/avatars/pollies_2.png ",
}),
"https://cdn.example.com/avatars/pollies_2.png",
);
});
26 changes: 2 additions & 24 deletions desktop/src/shared/avatars/gooseAppAvatarRefs.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
export const GOOSE_APP_AVATAR_REF_PREFIX = "app-avatar:" as const;

const APP_AVATAR_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;
const GOOSE_COLLECTION_ID_PATTERN = /^(fuzzies|gloopies|pollies)[-_](\d+)$/;

type ParseGooseAppAvatarOptions = {
allowFilenameFallback?: boolean;
};

function cleanAvatarCandidate(value: string): string {
const basename = value
Expand All @@ -19,7 +14,6 @@ function cleanAvatarCandidate(value: string): string {

export function parseGooseAppAvatarId(
value: string | null | undefined,
options: ParseGooseAppAvatarOptions = {},
): string | null {
const trimmed = value?.trim();
if (!trimmed) {
Expand All @@ -33,22 +27,13 @@ export function parseGooseAppAvatarId(
return APP_AVATAR_ID_PATTERN.test(id) ? id : null;
}

if (options.allowFilenameFallback) {
const candidate = cleanAvatarCandidate(trimmed);
const collectionMatch = GOOSE_COLLECTION_ID_PATTERN.exec(candidate);
if (collectionMatch) {
return `${collectionMatch[1]}-${collectionMatch[2]}`;
}
}

return null;
}

export function toGooseAppAvatarRef(
value: string | null | undefined,
options: ParseGooseAppAvatarOptions = {},
): string | null {
const id = parseGooseAppAvatarId(value, options);
const id = parseGooseAppAvatarId(value);
return id ? `${GOOSE_APP_AVATAR_REF_PREFIX}${id}` : null;
}

Expand All @@ -67,15 +52,8 @@ export function resolveImportedPersonaAvatarUrl({
avatarDataUrl?: string | null;
avatarRef?: string | null;
}): string | null {
const trimmedAvatarRef = avatarRef?.trim();
const avatarRefFileFallback =
trimmedAvatarRef && !isPersistableAvatarUrl(trimmedAvatarRef)
? toGooseAppAvatarRef(trimmedAvatarRef, { allowFilenameFallback: true })
: null;
const gooseRef =
toGooseAppAvatarRef(avatarRef) ??
avatarRefFileFallback ??
toGooseAppAvatarRef(avatarDataUrl);
toGooseAppAvatarRef(avatarRef) ?? toGooseAppAvatarRef(avatarDataUrl);
if (gooseRef) {
return gooseRef;
}
Expand Down
Loading