remove the desktop-pet system
Build / build (push) Canceled after 0s
Playwright / e2e (push) Canceled after 0s

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.
This commit is contained in:
2026-10-20 13:24:17 +08:00
committed by trae-solo-agent
parent 5e1003ceb7
commit b709ec6742
30 changed files with 64 additions and 1793 deletions
@@ -0,0 +1,59 @@
---
date: 2026-10-20
pr: pending
feature: Remove the desktop-pet system (pet-state socket, Petdex catalog, pets store)
impact: 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.