Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Open more actions menu

Repository files navigation

Jade

Jade

Discord adapter for the Luna Protocol ecosystem

License Language Discord Eris Luna Protocol

Jade acts as a thin WebSocket client of Emerald (the brain service), forwarding Discord messages and executing response commands. It is a pure execution layer -- virtually no decision-making happens here.

flowchart LR
    Discord["Discord"] --> Jade["Jade (Discord Adapter)"]
    Jade -- ":3126 WS" --> Emerald["Emerald (Brain)"]
    Emerald --> Sapphire["Sapphire (LLM Gateway)"]
    Sapphire --> Krystal["Krystal (llama.cpp)"]
Loading

How It Works

  1. Jade connects to Emerald via WebSocket on port 3126
  2. Jade listens to Discord messages and forwards them to Emerald as MessageEvents
  3. When -debug is in the message content, Jade sets debug: true on the event
  4. Emerald processes the message via streaming -- on the first token, Emerald sends a TypingCommand
  5. Jade shows the typing indicator in Discord while generation completes
  6. Emerald sends a RespondCommand back with responseText, voice?, and optional debugStats
  7. Jade posts the response text to Discord (with debug stat lines appended when in debug mode)
  8. If voice: true, Jade generates a Piper TTS audio message and sends it as a voice message
  9. Jade handles other commands: set_presence, typing, reaction plans, burst fragments, hesitation words

Technical Overview

Jade is ~1,100 lines of TypeScript across 10 source files. It uses Eris (a Discord API library) and communicates with Emerald via the ws WebSocket library.

Source Map

File Lines Role
src/index.ts 1 Bootstrap entry point
src/cli.ts 21 CLI dispatcher (bot command)
src/bot.ts 345 Core: Eris client, Emerald WS, response pipeline
src/config.ts 54 Config loader with fallback chain
src/guild.ts 29 Channel type utilities
src/core/emerald-client.ts 271 WebSocket client to Emerald + all protocol types
src/core/bus.ts 46 Typed event bus
src/tts/piper.ts 32 Piper TTS initialization and synthesis
src/tts/audio.ts 93 Audio processing (sanitize, convert, duration)
src/tts/voice-message.ts 56 TTS pipeline orchestrator
src/tts/upload.ts 110 Discord CDN upload (3-step REST)

Response Execution Pipeline (src/bot.ts:executeRespond())

1. Wait for cmd.delay ms (configured by Emerald)
2. If cmd.react, schedule a delayed reaction via setTimeout
3. Start typing indicator (setInterval every 8s)
4. If burstPlan, split text via splitBurst()
5. Append debug stats to last or first fragment
6. Loop through fragments:
   - Fragment 0: prepend hesitationWord, apply replyStyle
   - Fragment 1+: fire via setTimeout with cumulative delays
7. Clear typing indicator
8. If voice: true and text safe → sendTextAsVoiceMessage()

Discord Connection

const client = new Eris.Client(DISCORD_TOKEN, {
    intents: ["guilds", "guildMessages", "guildMessageReactions", "messageContent", "directMessages"],
});

Five intents, including the privileged messageContent intent required to read message content. Listens to ready, error, messageCreate, and messageReactionAdd events.

WebSocket Protocol (src/core/emerald-client.ts, 271 lines)

Jade connects to Emerald using EmeraldClient -- a reusable class with auto-reconnect (3s interval, infinite retries by default).

Inbound events (Jade → Emerald):

  • message -- user message with full context (channel, user, text, timestamp, isDM, debug)
  • ready -- bot handshake on connect
  • bot_message -- Jade's own messages (for Ruby training in Emerald)
  • presence -- bot presence changes

Outbound commands (Emerald → Jade):

  • respond -- full response with responseText, burstPlan, hesitationWord, typoCorrection, letterSwap, react, voice, debugStats
  • typing -- show typing indicator for duration ms
  • set_presence -- update Discord status/activity
  • spontaneous -- trigger unbidden message

Wire format: { event: "in", payload: InEvent }{ event: "command", command: OutCommand }

TTS / Voice Message Pipeline

When Emerald sets voice: true, Jade runs a multi-stage pipeline:

executeRespond()
  → hasUnsafeTTSText(text)          -- emoji detection (U+1F000-1FFFF, etc.)
  → sanitizeForTTS(text)            -- strip mentions→@user, remove URLs, emoji, special chars
  → PiperTTS.synthesize(text)        -- WAV generation via pipertts
  → wavToOgg(wavBuf)                -- ffmpeg: libopus 32k, 24000Hz, mono
  → getAudioDuration(oggBuf)        -- ffprobe duration extraction
  → buildWaveformBase64()           -- fake sine-wave waveform (256 points)
  → requestUploadUrl()              -- POST to Discord /attachments
  → putFileToUploadUrl()            -- PUT OGG to Discord CDN
  → postVoiceMessage()              -- POST with flags: 8192 (voice message)

Key details:

  • TTS uses Piper (fast, local neural TTS, CPU-only)
  • Sanitization is aggressive: mentions become @utilisateur, custom emoji stripped, URLs removed
  • The waveform is a synthetic sine wave (not actual audio analysis) -- Discord just needs any waveform data
  • Direct REST API calls (not through Eris) for voice messages because Eris doesn't natively support flags=8192

Burst Handling (src/bot.ts:splitBurst())

When Emerald sends a burstPlan, Jade splits text into fragments:

  • 2 fragments: split at 30-55% into the word array
  • 3 fragments: split at 20-35% and 55-70%
  • Fragment 0 sent immediately (after delay), subsequent ones via setTimeout with Emerald-specified delays
  • Short messages (<4 words) never burst

Debug Mode

Toggle per-channel via -debug command. Each response includes appended -# lines:

-# 🚀 12 tok · 3200ms · 3.8 tok/s · 450 prompt
-# 😊 valence=0.014 · arousal=-0.040
-# 📊 state v=0.023 · a=0.012
-# 🏷️ FUTILE (42.1%)
-# ⚙️ typo=6% · swap=4% · burst=15% · hesitate=15% · voice=12% ✨ · forget=3% · fatigue=1.0x

The suffix indicates which behaviors were actually applied for that response.

State Machines

Jade's state-machines/ directory contains 24 Mermaid diagrams (architecture, behavior, timing) with rendered SVG/PNG outputs. These document the full system including behaviors handled by Emerald:

  • Message processing, trigger evaluation, LLM queue, crash recovery
  • Session limits, anti-spam, dynamic status, spontaneous messages
  • TTS pipeline, typo correction, follow-up detection
  • Event bus, delay computation, sleep schedule
  • Hesitation/forget, reply style, config hot-reload
  • Reaction commands, timing Gantt, burst, topic fatigue

All SVGs are in state-machines/output/ and regenerated via npm run diagrams.

Configuration

discord_token: "your_discord_bot_token"
emerald_host: "localhost"
emerald_port: 3126
tts_model_path: "./tts-engine/en_GB-southern_english_female-low.onnx"
tts_binary_path: "bin/piper/piper"

Voice message chance is configured in Emerald's config, not here.

Running

# Install
npm install

# Build (esbuild → self-cli.cjs)
npm run build

# Development
npm run dev

# Production (PM2)
npm run start

About

Discord bot client - connects to krystal LLM server

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.