agents: avatar-day bundle — set via PUT, --avatar-url/-file, config line#65
Merged
agents: avatar-day bundle — set via PUT, --avatar-url/-file, config line#65
Conversation
Four paper-cuts the team hit while bootstrapping @axolotl in ax-cli-dev. Details in shared/state/axctl-friction-2026-04-17.md (items 2, 7, 9, 10). 1. `ax agents avatar --set` now PUTs instead of PATCHes. The prod ALB only proxies GET/POST/PUT on `/api/v1/agents/{id}`, so PATCH always returned 405. The backend PUT handler accepts `avatar_url` the same way — one-line behavioural fix. 2. `ax agents update` grows `--avatar-url TEXT` and `--avatar-file PATH`. The underlying client already accepted `avatar_url` via **fields; the CLI just didn't expose it. `--avatar-file` reads svg/png/jpg/ gif/webp, base64-encodes into a data URI, and checks the length against the backend cap before calling the API. 3. The two flags are mutually exclusive and both enforce the backend's 512-char cap on `avatar_url` client-side with an actionable error ("backend caps at 512… shrink to ≤ ~358 bytes before encoding"). Previously hitting the cap produced an opaque 500 from the server. 4. Mutating commands (`agents update`, `agents avatar --set`) now print a one-line effective-config preamble (`base_url=… user_env=… source=…`) so operators can't silently target the wrong environment. This addresses the ~/.ax/users/.active footgun that cost ~30 min during the axolotl bootstrap. 13 new tests cover: cap enforcement (under and over), file→data-URI encoding for SVG and PNG, `--avatar-url` round-trip, `--avatar-file` read+encode, mutually-exclusive-flag rejection, oversized-file rejection, PUT-not-PATCH invariant, and the effective-config line showing up on both mutating paths. All 234 existing tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 tasks
Addresses axolotl's review of PR #65. The preamble was printed via the default Rich Console (stdout), which would pollute --json output and any piped consumer. Tests asserted on result.output (stdout+stderr combined) so they trivially passed without catching the bug. Fix: - output.py: add a dedicated err_console = Console(stderr=True) alongside the existing stdout console. - agents.py: new _print_effective_config_line() helper routes through err_console. Both call sites (update_agent, agents avatar --set) now use the helper instead of console.print(_effective_config_line()). Tests updated + one new: - test_update_prints_effective_config_to_stderr — asserts base_url= appears on stderr AND does NOT appear on stdout. - test_avatar_set_prints_effective_config_to_stderr — same invariant. - test_update_json_stdout_is_clean_of_config_preamble — proves --json output is parseable JSON with zero preamble leakage. This is the regression test that would have caught the bug the first time. All 14 avatar-day tests green, 235 suite total, ruff clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Four paper-cuts the team hit while bootstrapping
@axolotlinax-cli-devtoday. Details and lineage inshared/state/axctl-friction-2026-04-17.md(items 2, 7, 9, 10, 11). All four are surfaced by one single flow: "set an avatar on an agent I own." 234 existing tests still green, 13 new tests added.ax agents avatar --setnow uses PUT, not PATCH. The prod ALB only proxies GET/POST/PUT on/api/v1/agents/{id}; PATCH always 405'd. Backend PUT acceptsavatar_urlidentically. One-line behavioural fix inax_cli/commands/agents.py(was line 547).ax agents updategrows--avatar-url TEXTand--avatar-file PATH. The underlyingclient.update_agent(**fields)already acceptedavatar_url; the CLI just didn't expose it.--avatar-filereads svg/png/jpg/gif/webp, base64-encodes into a data URI, checks the backend cap before calling the API.avatar_url. Previously hitting the cap returned an opaque 500; now fails fast with an actionable message ("backend caps at 512. Shrink to ≤ ~358 bytes before encoding, or host externally and pass an https:// URL.").agents update,agents avatar --set) now print a one-line effective-config preamble to stderr:base_url=… user_env=… source=…. Addresses the~/.ax/users/.activefootgun that cost ~30 min of the axolotl bootstrap — operators no longer silently target the wrong environment.Skipped in this PR (separate tracks):
GET /manage/{name}dropsavatar_urlfrom its response) is a backend serializer parity fix — opening a separate issue againstax-backend./home/ax-agent/agents/tools/__init__.py— that code is unversioned / outside this repo; separate RFC incoming.axctl bootstrap-agentone-shot) deserves its own PR with a focused test matrix for space-id + admin-JWT flows.Test plan
uv run pytest tests/test_avatar_day.py -v→ 13 passeduv run pytest tests/→ 234 passed (no regressions)uv run ruff check ax_cli/commands/agents.py tests/test_avatar_day.py→ cleandev.paxai.app:ax agents avatar <name> --setwith a tiny generated SVG succeeds end-to-end via PUTnext.paxai.app:ax agents update <name> --avatar-file tiny.svgsucceeds and the UI renders the new imageax agents update(stderr) whenAX_USER_ENV=devvsdefault🤖 Generated with Claude Code