Inspiration
Every active subreddit eventually hits the same wall: the volume of reports, modmail, and edge-case threads grows faster than the mod team can. The mods I talked to weren't asking for an AI to replace them: they were asking for a teammate who could draft the warning, sort the modqueue, and write the daily brief so they could spend their time on the calls that actually need a human. SubPilot is that teammate.
I was also inspired by the "Pixel Agents" style of visualizing AI agents as little characters in a virtual office. Most mod tools feel like a faceless backend. I wanted SubPilot to feel like a real team you watch work.
What I learned
- The Devvit platform as a product surface. Per-install Redis isolation, subreddit-scoped settings, the webview/server split, the mod menu and mod-log triggers — Devvit gives you a lot if you lean into it. I learned how to wire each surface (chat console, mod menu, public @-mention, scheduled job) into a single shared agent registry instead of duplicating prompts.
- Multi-agent prompting at a token budget. I ended up with 16 specialized agents (chat, triage, risk-analyst, warning-drafter, brief, thread-summarizer, four summon agents, five automation agents, plus a health ping). They share a tiny base prompt (~150 tokens) and pay only for the fragments they actually need. A rule-based router picks the right specialist before any LLM call.
- Free-tier survival. Gemini's free tier is generous but not infinite. I built per-subreddit Redis-backed rate/token budgets, priority bands (a mod typing in chat is "high", a chatty automation is "normal"), tier fallback (deep → balanced → lite if the configured model rate-limits), and aggressive response caching for the agents whose answers are stable for hours (briefs, translations, thread summaries, the ping).
- Data isolation is a platform guarantee, not a feature you bolt on. I audited every Redis key SubPilot writes and documented exactly how two subreddits running the same install never see each other's data — even when the same mod account is on both teams.
How I built it
- Front end: Vanilla TypeScript + Vite webview, served by Devvit. A single Mod Console with chat history, quick-action chips, live feed, sub-switcher, plugins page, automations page, and the Playground.
- Back end: Hono routes on Devvit's server runtime. Every AI call funnels through one
runAgent()runner that resolves the model, builds the prompt, checks cache, checks the rate/token budget, calls Gemini, records usage, stamps activity, and returns a structured result. - Agent registry (
src/core/ai/agents.ts): Each agent declares its model tier, temperature, max tokens, priority, whether it can emit actions, and abuildPrompt()function. New agents are ~20 lines. - Context loaders: Subreddit rules, description, tone preference, and detected third-party mod tools are fetched once and cached. A separate "live grounding" block (top-risk users + recent mod actions from the last 24h) is injected into the agents that need it.
- Plugin discovery: A declarative registry of known mod bots (AutoModerator, RemindMeBot, etc.) plus runtime signal detection — mod-team membership, wiki pages, mod-log patterns. Detected tools become hints in the AI context so SubPilot knows what's already installed and avoids stepping on those workflows.
- Playground / Pixel Agents: A self-contained client module renders 8 pixel-art characters on top of the office art — one per agent role. They wander when idle, walk to their desks when their server-side state flips to "working", and play a typing animation. Tooltips show the live activity summary, model, and timestamp.
Challenges I ran into
- Devvit settings vs CLI.
gemini-keyis a subreddit-scoped install setting, not an app-global setting, which meansdevvit settings setrejects it. The right path is the Reddit Mod Tools UI atdevelopers.reddit.com/r/<sub>/apps/<app-name>. I now ship that path in the docs and the in-app empty state. - The Devvit webview sandbox.
window.confirm()silently no-ops inside the webview iframe. My chat-delete button "didn't work" for a session until I traced the issue and replaced it with an inline two-tap confirmation directly in the kebab menu. - Vite asset paths. Static assets needed to live under
src/client/public/to be copied verbatim and referenced as absolute/playground_assets/...paths instead of relative paths that Vite was flattening. - Token economy. First pass had every chat turn refetching subreddit rules and live grounding. Caching contexts per-install and skipping grounding for agents that don't need it cut average token usage by ~60% without changing answer quality.
- Making the Playground feel alive without a game engine. Eight characters, four animation states (idle/walk/sit/work), per-character zones, wandering AI, and 9 pre-rendered SVG frames per character — all in a single requestAnimationFrame loop with no dependencies. Z-layering "behind the desk" is faked by rendering a half-body sprite that's anchored to the desk surface — no per-pixel collision needed.
Built With
- css
- devvit
- eslint
- html
- javascript
- node.js
- prettier
- typescript
Log in or sign up for Devpost to join the conversation.