--- 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:` 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 `/profile-metadata//pets/`. Deleting the code does not remove those files; they can be deleted manually.