fix(avatars): bundle a default avatar with each first-run template (#2693) - #2776
Open
obasilakis wants to merge 2 commits into
Open
fix(avatars): bundle a default avatar with each first-run template (#2693)#2776obasilakis wants to merge 2 commits into
obasilakis wants to merge 2 commits into
Conversation
…2693) A fresh install seeds scout/sage/scribe with only an avatar_prompt, and generation needs GEMINI_API_KEY, which no new instance has — so the first screen an operator sees is a fleet of initials. A local: template may now ship avatar.webp/avatar.png beside template.yaml. At create, when the template also declares avatar_prompt, the image is re-encoded through optimize_avatar into /data/avatars before the prompt is seeded as a DEFAULT avatar, so Generate Default Avatars still overwrites it once a key exists. Offline, no outbound call. A missing, oversized or undecodable image degrades to the old prompt-only seed. The three starter templates ship hand-drawn schematic glyphs (no Gemini key was available to render the prompts, and the schematic family matches the approved first-run illustrations). scribe gains the avatar_prompt it lacked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VKzc3MG4Fa1sJ1KuMmBtcV
5 tasks
| template_dir = _resolve_local_template_dir(config.template[6:]) | ||
| for filename in _BUNDLED_AVATAR_NAMES: | ||
| source = template_dir / filename | ||
| if not source.is_file() or source.stat().st_size > _BUNDLED_AVATAR_MAX_BYTES: |
| template_dir = _resolve_local_template_dir(config.template[6:]) | ||
| for filename in _BUNDLED_AVATAR_NAMES: | ||
| source = template_dir / filename | ||
| if not source.is_file() or source.stat().st_size > _BUNDLED_AVATAR_MAX_BYTES: |
| from PIL import Image | ||
| from utils.image_optimize import optimize_avatar | ||
|
|
||
| data = source.read_bytes() |
| if img.width * img.height > 4096 * 4096: | ||
| raise ValueError(f"bundled avatar is {img.width}x{img.height}, max 4096x4096") | ||
| _AVATAR_DIR.mkdir(parents=True, exist_ok=True) | ||
| (_AVATAR_DIR / f"{config.name}.webp").write_bytes(optimize_avatar(data)) |
…it (#2693) CodeQL raised four py/path-injection alerts on _install_bundled_avatar: the template dir and agent name are already contained by _resolve_local_template_dir and name validation, but the analyzer cannot follow those callees. Normalize and prefix-check the final source and destination paths inline, the same barrier routers/avatar.py uses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VKzc3MG4Fa1sJ1KuMmBtcV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #2693.
A fresh install seeds
scout/sage/scribewith only anavatar_prompt, and generation needsGEMINI_API_KEY, which no new instance has — so the default fleet renders as initials.Decision taken on the open question in the issue (bundled generated portraits vs hand-drawn art vs code placeholder): the ticket's preferred mechanism — a static image bundled with each shipped template — with hand-drawn schematic art as the image. No Gemini key was available to render the prompts, and the schematic family is the one already approved for first-run illustrations (ent#581).
local:template may shipavatar.webp/avatar.png(≤2 MB, ≤4096×4096) besidetemplate.yaml.avatar_prompt,crud._install_bundled_avatarre-encodes it throughoptimize_avatarinto/data/avatars/{name}.webp, then seeds the prompt as a default avatar (is_default_avatar=1). Re-encoding matters: the deploy-local root holds user-uploaded templates and the avatar route is unauthenticated.scribegains theavatar_promptit lacked (AC4); image is only installed alongside a prompt, so every bundled face stays regenerable and the agent-detailavatar_url(which requires a prompt) is populated.Tests
tests/unit/test_1484_create_agent_characterization.py:test_2693_bundled_avatar_installed_as_default— WebP lands in the store, row written as default with the template prompttest_2693_corrupt_bundled_avatar_keeps_prompt_seed— bad image: no file, prompt seed still happenstest_2693_first_run_fleet_ships_bundled_avatars— every agent indefault-system.yamlhas anavatar_promptand a decodableavatar.webpBoth behaviour tests fail with the
crud.pychange stashed; full file 45 passed. Template/manifest/gitignore suites (438) pass.Not run: live create on a real stack.
Not covered
github:Abilityai/cornelius, not from the bundled manifest, so it still shows initials until a key exists. Covering it means reading a bundled image from a GitHub template — a follow-up if wanted.startup.shcopies the whole template dir into the workspace, soavatar.webp(~10 KB) also lands in/home/developer. Harmless; not filtered.🤖 Generated with Claude Code
https://claude.ai/code/session_01VKzc3MG4Fa1sJ1KuMmBtcV