Loom
Overview
Section titled “Overview”Interactive rulebook companion for tabletop RPGs. A Hono REST API + React SPA for chatting with ingested rulebooks. Features multi-voice TTS audio generation via Kokoro, campaign management, session-based conversations, and full Cairn memory integration (observations, memories, knowledge graph).
How it works
Section titled “How it works”Boot sequence (src/main.ts)
Section titled “Boot sequence (src/main.ts)”- Run DB migrations (Cairn tables + campaigns/sessions)
- Create Kysely DB via
@repo/db - Start Hono server on configured port (default: 4900)
- Register graceful shutdown on SIGINT/SIGTERM
Agent (src/agent.ts)
Section titled “Agent (src/agent.ts)”processMessage() handles each chat turn:
- Look up session + campaign context
- Create
MemoryManagerfrom@repo/cairn - Hybrid memory recall: rules (by category) + campaign memories (by embedding similarity)
- Build context preamble with timezone, mode (play/recap), campaign info, observations, rules, memories
- Replay conversation history into pi-agent-core Agent
- Run agent with GM system prompt (dice protocol, TTS-friendly format rules)
- Async post-response: observe -> promote -> reflect (same Cairn pipeline as Construct)
The system prompt enforces TTS-friendly output: no markdown, no code blocks, no em dashes, hyphens spelled out.
Rulebook Ingestion (src/ingest.ts)
Section titled “Rulebook Ingestion (src/ingest.ts)”CLI script to chunk and ingest rulebooks into the memory pipeline:
- Scans
RULES_DIRfor.mdand.txtfiles - Splits on heading hierarchy, respects ~1500 token limits
- Stores each chunk as a
category: 'rules'memory with embedding - Runs async graph extraction (NPCs, locations, items, spells, etc.)
- Progress logging per file
Run via just loom-ingest.
Text-to-Speech
Section titled “Text-to-Speech”Three-layer TTS pipeline:
Kokoro client (src/tts/kokoro.ts):
- OpenAI-compatible API client for Kokoro FastAPI server
- Handles streaming and buffered synthesis
- Text cleaning: strips code, tables, dice notation, stat blocks, markdown
Scriptify (src/tts/scriptify.ts):
- LLM-powered script adaptation for multi-voice narration
- Parses
[SPEAKER]tagged output, maps characters to configured voices - Falls back to single-voice on parse failure
- Tracks usage
Voices (src/tts/voices.ts):
- Static voice catalog with quality grades
- Blend expression builder (e.g.,
af_heart(0.7)+af_sky(0.3)) - Live voice detection from Kokoro server
Audio assembly (src/routes/audio.ts):
- Multi-voice parallel synthesis per segment
- In-memory audio cache with TTL cleanup
- Falls back gracefully if TTS disabled
Docker (docker-compose.yml)
Section titled “Docker (docker-compose.yml)”Single service: Kokoro FastAPI TTS (CPU-optimized v0.2.4). Port 8880. Model volume persistence.
API Routes
Section titled “API Routes”POST /api/chat
Section titled “POST /api/chat”SSE streaming endpoint. Events:
delta— Incremental text chunksdone— Final message with full textaudio— TTS audio stream URL (if enabled)error— Error message
GET /api/chat/tts-stream/:id
Section titled “GET /api/chat/tts-stream/:id”Serves cached audio for a completed TTS generation.
Campaigns (/api/campaigns)
Section titled “Campaigns (/api/campaigns)”GET /— List all campaignsPOST /— Create campaign (name, description, system)GET /:id— Campaign detailPATCH /:id— Update campaignPOST /:id/sessions— Create new session for campaign
Sessions (/api/sessions)
Section titled “Sessions (/api/sessions)”GET /:id— Session detail with messagesPATCH /:id— Update session metadataGET /:id/observations— Session observations from Cairn
Settings (/api/settings)
Section titled “Settings (/api/settings)”GET /voices— Voice catalog from KokoroGET /voice-config— Per-campaign voice assignmentsPUT /voice-config— Store voice configPOST /voice-preview— Generate voice sampleGET /characters/:sessionId— Extract character names from graph + recent messages
Debug (/api/debug)
Section titled “Debug (/api/debug)”POST /scriptify— Test scriptify pipelinePOST /synthesize-segment— Test single segment synthesis
Database
Section titled “Database”Six migrations (Cairn base + Loom additions):
| Migration | Description |
|---|---|
001-initial | Cairn base tables |
002-fts5-and-embeddings | FTS5, triggers, embeddings |
003-graph-memory | Graph nodes + edges |
004-observational-memory | Observations table, watermarks |
005-observation-promoted-at | Promoter tracking |
006-campaigns | campaigns + sessions tables with FK to conversations |
Key Files
Section titled “Key Files”| File | Role |
|---|---|
src/main.ts | Entry point, migrations, server start |
src/server.ts | Hono app setup, CORS, route mounting, static serving |
src/agent.ts | Chat agent with rulebook context + Cairn memory |
src/system-prompt.ts | GM system prompt, dice protocol, TTS format rules |
src/ingest.ts | Rulebook ingestion: chunking, embedding, graph extraction |
src/env.ts | Zod-validated env config |
src/tts/kokoro.ts | Kokoro TTS client (OpenAI-compatible API) |
src/tts/scriptify.ts | LLM script adaptation for multi-voice narration |
src/tts/voices.ts | Voice catalog and blend expressions |
src/routes/chat.ts | SSE chat streaming + TTS audio serving |
src/routes/campaigns.ts | Campaign CRUD |
src/routes/sessions.ts | Session detail + observations |
src/routes/audio.ts | Audio caching, voice config, multi-voice synthesis |
src/routes/settings.ts | Voice catalog, config, character extraction |
src/routes/debug.ts | TTS debugging endpoints |
src/db/schema.ts | Campaign + session table types |
src/db/queries.ts | DB operations |
Frontend (web/)
Section titled “Frontend (web/)”React 19 SPA with React Router. Built with Vite.
- CampaignList — Browse/create campaigns
- CampaignView — Campaign detail with sessions
- PlayView — Chat panel + session sidebar (main play interface)
- ChatPanel — Message list with SSE streaming + auto-play audio
- VoiceSettings — Per-campaign voice assignments with preview
- TtsDebug — Developer TTS testing
Hooks: useChat (SSE streaming + message state), useSession (metadata), useApi (generic fetch).
Integration Points
Section titled “Integration Points”- @repo/cairn — Full memory pipeline: observe/reflect/promote/graph after each turn. Rules stored as memories. Graph extraction for NPCs/locations.
- @repo/db —
createDb()for database connection, migration runner. - Kokoro — Self-hosted TTS via Docker. CPU-optimized, multi-voice.
- OpenRouter — LLM inference for agent + scriptify + memory workers.
Running
Section titled “Running”just loom-dev # Backend dev mode (file watching)just loom-web # Frontend Vite dev server (proxies to :4900)just loom-ingest # Ingest rulebooks from RULES_DIRjust loom-start # Production (build web + start server)Environment: .env.loom (see .env.loom.example).