The pet feature was three loosely coupled subsystems that no core flow depended on: the Petdex catalog page, the pet install/preference store, and the pet-state socket. All three are removed. The only cross-boundary touch point was a try/catch-wrapped observer that mapped chat-run events to pet animations. Removing it leaves the /chat-run event contract unchanged: no event name, field name, field type, or emission timing changed. Removing petdexPublicRoutes also closes the unauthenticated outbound proxy at /api/hermes/petdex/asset, which shrinks the SSRF surface.
3.3 KiB
date, pr, feature, impact
| date | pr | feature | impact |
|---|---|---|---|
| 2026-10-20 | pending | Remove the desktop-pet system (pet-state socket, Petdex catalog, pets store) | The optional "宠物" sidebar entry and the whole pet subsystem are gone. The only cross-boundary touch point was a try/catch-wrapped observer that mapped chat-run events to pet animations; removing it does not change any /chat-run event name, field name, field type, or emission timing. No chat behavior changed. |
Remove the desktop-pet system
What changed
The pet feature was three loosely coupled subsystems that no core flow depended on. All three are removed.
| Area | Removed |
|---|---|
| Client | views/hermes/PetdexView.vue, stores/hermes/pets.ts, stores/hermes/pet-state.ts, api/hermes/pets.ts, api/hermes/petdex.ts, api/hermes/pet-state.ts, public/icons/pet-resize.svg (orphan) |
| Client wiring | router/index.ts route hermes.petdex, AppSidebar.vue nav item, sidebar.petdex + petdex.* i18n keys in zh.ts / en.ts |
| Server | services/hermes/pets.ts, services/hermes/petdex.ts, services/hermes/pet-state-socket.ts, controllers/hermes/pets.ts, controllers/hermes/petdex.ts, routes/hermes/pets.ts, routes/hermes/petdex.ts |
| Server wiring | routes/index.ts (three app.use registrations), index.ts (PetStateSocketServer import, field, and init()) |
| Chat chain | run-chat/index.ts and run-chat/handle-bridge-run.ts pet observation calls, resolvePetEventProfile, observePetEvent |
| Tests | tests/server/pets-service.test.ts, tests/server/petdex-service.test.ts, tests/client/pets-store.test.ts, /api/hermes/pets/active mock in tests/e2e/fixtures.ts |
Why it was safe
The pet subsystem sat on a single boundary crossing: the chat-run event
stream. run-chat/index.ts (emitExternalEvent, emitToSession) and
run-chat/handle-bridge-run.ts (two emit closures) each called
observeRunChatPetEvent(profile, event, tagged) to translate a chat event
into a pet animation state. Every call was wrapped in a try/catch that only
logged at debug level, so a pet failure could never break a run.
Removing those calls leaves the surrounding emit paths intact: the tagged
payload, the session:<id> room broadcast, the direct socket.emit fallback,
the waiter wake-ups, and the session event buffer are all unchanged.
Everything else was an island: the client pets store was consumed only by
PetdexView, and the client pet-state store had no callers at all — the
client never connected to the /pet-state namespace, so the server-side
pet.state.snapshot / pet.state.updated events had no consumer.
Not changed
The /chat-run Socket.IO contract is untouched: no event name, field name,
field type, or emission timing changed. This fragment exists because the
touched files sit on the chat chain path, not because any chat behavior moved.
Side effect
routes/index.ts registered petdexPublicRoutes before the auth middleware,
which exposed /api/hermes/petdex/asset as an unauthenticated outbound proxy
(with a petdex.dev host allowlist). Removing it closes that endpoint and
shrinks the SSRF surface.
Local data
Persisted pet state lives under
<HERMES_WEB_UI_HOME>/profile-metadata/<base64url(profile)>/pets/. Deleting
the code does not remove those files; they can be deleted manually.