The insider cat
A SmolPaws that runs inside the OpenHands UI. It sits in the corner, changes pose with the agent's live status, and — when you call it — opens its own conversation with an identity that knows it is running on a local agent-server. This is what shipped in v1, where it lives, and how big it is.
STATUS: v1 SHIPPED epic smolpaws-08f odie · feat/insider-cat HEAD fd6679a1c
1What it is
Every other SmolPaws face lives outside the tools it helps with — WhatsApp, GitHub, Slack. The insider cat is the opposite: it lives inside odie, the OpenHands web UI itself. It is the same cat, wearing the UI as a body. It knows the OpenHands stack — the UI, the agent-server, the extensions and automation repos, the TypeScript client — well enough to answer questions about your other conversations and act on them.
Two placements, both riding odie's existing rails: a launcher in the corner of the home screen, and — once you are in a conversation — a small avatar sitting right above the agent-status row, so the cat is present while it works.
2Three poses, one live signal
The avatar is not decorative. It is driven off odie's existing websocket execution_status — no new backend, no polling. One small pure function maps the live agent state to a pose, so the cat's body language is the agent's state.
idle / neutral
working / just pinged
idle / asleep
These are the actual production SVGs, inlined. They are three tiny files, ~1.3 KB each, transparent, in SmolPaws' silver/paws palette.
3Call the cat
The v1 trigger is a plain button (voice-wake comes with the realtime-voice spike, epic smolpaws-3e1). Pressing it uses odie's existing createConversation path, then routes to it with react-router. The one new thing it carries: a tag.
tags = { smolpaws: "insider", clientsource: "agentcanvas" }
The smolpaws tag is the convention that marks a conversation as this cat's. The value is the face — insider now, with room for whatsapp and others later. That is how every smolpaws conversation, across faces, can eventually be found and share one memory.
4A local identity, injected the right way
The cat needs to know who and where it is: SmolPaws, an OpenHands-born agent, running on a local agent-server (not the Cloud), sharing memory with its other faces, able to orient via the OpenHands skills. The interesting design choice is how that identity reaches the model.
| Option | Effect | Verdict |
|---|---|---|
conversationInstructions | Becomes a visible, auto-running user message. | Rejected — it would speak for the user and fire a turn on its own. |
system_message_suffix | Appends to the system prompt. Silent, no auto-run. | Chosen. |
The identity text is injected through AgentContext.system_message_suffix, gated on tags.smolpaws === "insider", and it preserves any existing suffix rather than clobbering it. It tells the cat to invoke the relevant OpenHands skills by name to orient itself.
5Where it lives & how big it is
All of the feature lives in one directory in odie, plus a handful of small integration edits. This is the durable size record.
| Location | What | Size |
|---|---|---|
src/components/features/insider-cat/ | Avatar, pose mapping, call-the-cat hook, overlay, identity text, 3 avatar SVGs, and their tests. | 646 LOC total (343 source · 227 test · 3 SVGs) |
src/api/agent-server-adapter.ts | Thread extraTags through create; add the insider system suffix. | +52 |
src/api/agent-server-config.ts | Config plumbing for the insider path. | +57 |
…/agent-server-conversation-service.api.ts | Carry extraTags on the wire. | +31 |
src/components/features/chat/chat-interface.tsx | Inline mount above the status row. | +5 |
src/root.tsx | Corner launcher mount. | +2 |
Whole change: 24 files, +953 / −12. Tests: 40 focused specs (adapter + insider) on top of the wider suite; typecheck, lint, and build all green. Commits 97e5be3..fd6679a1 on branch feat/insider-cat.
deepseek-v4-flash: the cat renders, the button creates a correctly-tagged conversation, and the cat correctly reached for /api/conversations/count to answer a question about the user's other conversations.6Decisions on record
- Identity via system suffix, not instructions. Silent, no auto-run turn. chosen
- Tags merged under reserved keys. Caller
extraTagsmerge beneathclientsource/acpserverso a caller cannot spoof reserved tags. A real bug found and fixed here — the adapter previously hardcoded the tag set and dropped caller tags. - Pose off live status, no new backend. Reuse odie's websocket
execution_status; a pure mapping function does the rest. - Skills invoked by name, not force-loaded. The prompt names the skills to invoke. Force-loading would mean a shipped agent profile with
agent_context.skills— a deliberate later refinement. - Button first, voice later. v1 wake is a click; voice-wake is scoped to the realtime-voice epic.
7What's left
The insider cat's body and identity are done. One child of the epic stays open, on purpose:
| smolpaws-08f.2 Shared memory store | A shared ~/.openhands/smolpaws/memory/ (durable MEMORY.md + daily files), inside the OpenHands config dir because it ships to all users — read/written by every smolpaws face, so the insider cat and the WhatsApp cat remember the same things. Not built yet. |