9c021a6bde
Selecting a self-generated pet in the Bot avatar picker always failed with "Could not load that pet — try another", and its tile showed only a name. Locally hatched pets have no petdex manifest entry, so pet.gallery reports an empty spritesheetUrl; the picker cropped frame 0 client-side from that URL and bailed on the empty string. The same raw CDN fetch also lacked a User-Agent, which the petdex CDN rejects with 403 (#90465), so manifest pets could fail on the same path. Route tile rendering and selection through the gateway's pet.thumb RPC, which already backs the Settings pet picker: it crops frame 0 server-side from the installed sheet on disk (or the host-validated CDN URL for uninstalled pets) and returns a same-origin PNG data URI. The client cache is keyed by slug, evicts failures so a blip never poisons a tile, and races a 15s deadline so a hung RPC cannot park a pending promise forever. Based on analysis from PR #90931, whose target (plugin.js) has since been decomposed into pet.tsx. Co-authored-by: m1k3s0 <44042865+m1k3s0@users.noreply.github.com>
Bundled plugins
Drop a <name>/plugin.{ts,tsx} here that default-exports a HermesPlugin and
it registers automatically at boot (vite glob in ../contrib/plugins.ts), with
the same inventory + live enable/disable contract as runtime plugins.
Keep this tree for real shipped plugins (and the small authoring fixtures that
dogfood the SDK). One-off demos that rebuild a core chrome piece 1:1 do not
belong here — they double the UI and confuse Capabilities ▸ Plugins. Publish those
in the companion
hermes-example-plugins
repo instead.
User- and agent-authored plugins load at runtime from
$HERMES_HOME/desktop-plugins/<name>/plugin.js (the disk door) — see the
hermes-desktop-plugins skill.