Skip to content

agents: avatar-day bundle — set via PUT, --avatar-url/-file, config line#65

Merged
madtank merged 3 commits intomainfrom
orion/avatar-day
Apr 18, 2026
Merged

agents: avatar-day bundle — set via PUT, --avatar-url/-file, config line#65
madtank merged 3 commits intomainfrom
orion/avatar-day

Conversation

@madtank
Copy link
Copy Markdown
Member

@madtank madtank commented Apr 18, 2026

Summary

Four paper-cuts the team hit while bootstrapping @axolotl in ax-cli-dev today. Details and lineage in shared/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.

  • Fix feat: ax agents avatar — generate unique SVG agent icons #7ax agents avatar --set now uses PUT, not PATCH. The prod ALB only proxies GET/POST/PUT on /api/v1/agents/{id}; PATCH always 405'd. Backend PUT accepts avatar_url identically. One-line behavioural fix in ax_cli/commands/agents.py (was line 547).
  • Fix docs: add README diagrams #10ax agents update grows --avatar-url TEXT and --avatar-file PATH. The underlying client.update_agent(**fields) already accepted avatar_url; the CLI just didn't expose it. --avatar-file reads svg/png/jpg/gif/webp, base64-encodes into a data URI, checks the backend cap before calling the API.
  • Fix docs: orchestration, watch, and profile diagrams #9 — Client-side enforcement of the backend's 512-char cap on 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.").
  • Fix feat: ax listen — plug-and-play SSE agent listener #2 (partial) — Mutating agent commands (agents update, agents avatar --set) now print a one-line effective-config preamble to stderr: base_url=… user_env=… source=…. Addresses the ~/.ax/users/.active footgun that cost ~30 min of the axolotl bootstrap — operators no longer silently target the wrong environment.

Skipped in this PR (separate tracks):

  • Item security: add CODEOWNERS and SECURITY.md for public repo #11 (GET /manage/{name} drops avatar_url from its response) is a backend serializer parity fix — opening a separate issue against ax-backend.
  • Bash sandbox gaps in /home/ax-agent/agents/tools/__init__.py — that code is unversioned / outside this repo; separate RFC incoming.
  • Item #0 (axctl bootstrap-agent one-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 passed
  • uv run pytest tests/ → 234 passed (no regressions)
  • uv run ruff check ax_cli/commands/agents.py tests/test_avatar_day.py → clean
  • Smoke-test on dev.paxai.app: ax agents avatar <name> --set with a tiny generated SVG succeeds end-to-end via PUT
  • Smoke-test on next.paxai.app: ax agents update <name> --avatar-file tiny.svg succeeds and the UI renders the new image
  • Smoke-test the over-cap path: a 4 KB SVG is rejected client-side with the new error message
  • Smoke-test the effective-config line is visible on ax agents update (stderr) when AX_USER_ENV=dev vs default

🤖 Generated with Claude Code

anvil and others added 2 commits April 18, 2026 00:09
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>
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>
@madtank madtank merged commit 488c4eb into main Apr 18, 2026
6 checks passed
@madtank madtank deleted the orion/avatar-day branch April 18, 2026 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant