Skip to content

Releases: eschatus/roll20-dm-mcp

v2.0.3 — failure visibility and name resolution

Choose a tag to compare

@eschatus eschatus released this 31 Aug 20:07

Pin this tag from the gem: #v2.0.3.

No Mod redeploy neededAI_RELAY_VERSION stays at 2.5.0.

Every issue in this release was filed by DM Whisper as a downstream consumer, and they share one theme: an operation that did not happen, reported as though it did.

Failures are now visible as failures (#190, #191, #192)

ToolResult gains isError, plus a fail() builder beside text()/json(). A handler that throws always got the flag from the SDK; one that returned a failure had no way to say so, so clients were left pattern-matching English prose. Prose is unchanged — this is additive.

resolve_aoe no longer swallows a failed sheet read into {bonus: 0, source: "none"}, which was byte-identical to "this creature has no save for that ability". It fails before the damage batch, so nothing is half-applied, and the report marks any save that was genuinely rolled on a flat d20.

Five swallowed reads now propagate, including two found while triaging that the issues hadn't named: getDoors/getPaths reported an unread page as "no walls" (which would tell a wall-placement QC pass the walls were never placed), and batch_import_maps treated an unread page list as "nothing exists" and re-created pages skipExisting was passed to protect.

A live bug this surfaced

Surfacing source immediately exposed a real defect underneath. The relay collapses an attribute with an empty max to a flat value rather than {current, max} — the common case for NPC save attributes — and resolveSaveBonus read only .current. Every monster whose sheet carries a save that way had been saving at +0 and taking the damage. get_character_attribute had always handled both shapes; this path never got the same treatment.

Reads report misses, and expose token class (#194, #196)

get_token returns an error on a miss instead of a null body with isError: false. list_tokens and get_token now carry tokenClass, so a client can finally tell a sidekick from a true PC — previously the server routed HP writes on a distinction it would not disclose.

A per-flavor familiar class was considered and rejected: familiars, animal companions and summons are all player-controlled NPCs, and none of them route differently. sidekick is documented to cover all of them.

Name resolution tolerates punctuation (#195)

"Bandit Captain, the Scarred" — the natural spoken form, and what speech-to-text produces — now resolves to Bandit Captain the Scarred. Case was already folded; punctuation is the same class of difference. Four forked copies of the same matcher now share one normalizer.

Widening only ever widens: ambiguity still refuses rather than guessing, and that now extends to identically-named tokens, which previously resolved silently to whichever came first.

Docs (#181)

NOTICE drops the D&D Beyond bridge claim and reframes the Ammann tactics-index section as historical, preserving every legal fact.


Not in this release: the epithet-pool rework (#199, PR #201) bumps the relay to 2.6.0 and is held back deliberately — it would warn every campaign about a stale deploy until each is redeployed by hand.

483 tests, clean build and lint.

DM Whisper 0.1.6

Choose a tag to compare

@eschatus eschatus released this 31 Jul 21:55

First release with a fine-tuned local specialist as a usable backend, plus the instrumentation to keep improving it from real play.

Local specialist (opt-in)

A QLoRA'd Qwen2.5-7B trained on grader-verified gold trajectories for this table's conventions. Measured on the golden suite at n=60: 72–78% vs Haiku's 57%, at ~1.1 s/turn vs ~3.0 s, running entirely offline from a 4.7 GB q4_k_m file. Untrained baseline was 22–25%.

Enable in %APPDATA%\DM Whisper\.env:

DMW_ENABLE_LOCAL_LLM=1
DMW_PROVIDER=ollama
DMW_OLLAMA_MODEL=dmw-7b-v2-q4

The local path serves exactly the 19-tool slimmed scope the model was trained on — train/serve parity matters, since it fit the training format hard.

Session instrumentation

Every turn now writes a replayable record to events.jsonl: full untruncated tool args, honest ok/error, a pre-turn board snapshot, and repairOf chains linking rephrasings. A repair chain's final turn is DM-accepted ground truth; the earlier ones are labelled negatives. Sessions become training data.

Campaign picker + maker

Setup tab gains a campaign dropdown and a register-new form that call MCP directly — no LLM in the path. Necessary because the specialist's scope excludes switch_campaign, and correct regardless: a dropdown can't mishear a campaign name.

Fixes

  • Death markers render everywheredead now uses Roll20's built-in red X instead of aliasing a custom marker that shows nothing in campaigns without the custom set installed.
  • #158 — the instrumentation logged successes as failures (mark_dying's own success text contains "3 failed saves"), which corrupted repairOf on exactly the dying/AoE turns that matter most. Now threads the real MCP isError flag.
  • Native ollama /api/chat provider so num_ctx and format actually apply (the /v1 shim silently ignores options — that bug invalidated an earlier "local models are unusable" verdict).

⚠️ Run npm run release:mod after installingai-relay.js changed (death marker + threshold automation).

DM Whisper 0.1.5

Choose a tag to compare

@github-actions github-actions released this 29 Jun 00:15
75e80ef

DM Whisper 0.1.5

Live-table tool reliability (the headline)

  • HP, condition, and AoE commands now apply reliably. Earlier builds could reject up to ~half of tool calls at the table (the model emitting damage as a string, stringified target lists, or wrong parameter names), leaving the DM to track HP by hand. update_token_hp / update_hp_many / resolve_aoe are hardened with worked examples and strict type guidance — verified at 100% schema-valid / 92% fully-correct on the eval suite.
  • kill_token — one command marks a creature dead and moves it to the map layer.
  • Agentic loop (on by default) — the gem no longer silently ends a turn on a spoken acknowledgement; if you state an outcome and nothing was applied, it follows through. Bounded so it never spins. Set DMW_AGENTIC_LOOP=off to revert, =full for an extra completeness check on compound turns.
  • Prompt-cache restructure + conversation-tail caching for faster, cheaper turns.

Campaign & setup

  • Switching campaigns by voice now updates the gem correctly. The active campaign is read from the connected server, so the roster, vocab, and pronoun panel reflect the new campaign immediately instead of showing the previous one.

Journal

  • File handouts into a journal folder via direct RTDB (fixes the Jumpgate field gap); set_journal_folder now verifies the write instead of reporting false success.

Stability

  • STT and server shutdown now wait for the child process to actually exit.

Developer / testing

  • Model-agnostic tool-call eval harnesses (eval:tools single-shot shape, eval:arc stateful board-effect), with eval-arc validating against the real tool schemas (no coercion) so the harness can't mask a malformed call.
  • End-to-end human test script covering all features.

DM Whisper 0.1.3

Choose a tag to compare

@eschatus eschatus released this 23 Jun 16:42

What's new

Browserless relay by default (#83 / #84)

Combat no longer depends on a browser. The Roll20 realtime transport (Firebase RTDB) is now the default — no Playwright required for live sessions. A packaged install ships no browser; if the RT token expires, Dusty tells you to reconnect rather than silently reaching for a Chromium that isn't there. Set ROLL20_TRANSPORT=browser to opt back into the legacy relay (dev only).

Minify-on-deploy for the Mod (#84)

npm run release:mod now minifies ai-relay.js via esbuild before deploying (~52% smaller), keeping it under Roll20's API sandbox size limit. Human-readable source stays in git; stripped version goes to the sandbox.

Confirm prompt + Dusty's voice (#82)

Write-confirmation requests now route into the gem's message channel as a distinguished pending entry.

DRW-Original as canonical soak campaign

Live relay soak now targets Dreams of the Red Wizards — Original (clean sandbox, no active PCs).

Known issues filed this release

  • #85 requireExisting guard bypassed by single-tab fallback
  • #86 MCP deploy_mod_script missing tabName
  • #87 tab name .includes() can match stale backup tabs
  • #88 30s hang per call when RT transport is down (circuit breaker removed)
  • #89–94 cleanup / hardening items from the #84 code review

Upgrade notes

Re-harvest your Roll20 RT token via the gem's Setup tab if you see a 'realtime transport failed' error. Run npm run release:mod to deploy the minified relay to your campaign.

DM Whisper 0.1.2

Choose a tag to compare

@github-actions github-actions released this 23 Jun 02:34

DM Whisper 0.1.2

The release where the installed app stands on its own — first-run onboarding, token harvest, mic capture, and STT all work end-to-end with no dev tree, no Python, and no Playwright.

Still unsigned (SmartScreen / Gatekeeper warnings; mac → right-click → Open). Signing + notarization is tracked for v1.

Highlights since 0.1.1

  • 🔑 Native token harvest — Connect Roll20 / D&D Beyond now work in the installer. Login happens in an Electron window (the Chromium we already ship), no Playwright/Chromium download. (#66)
  • 🎙️ Cleaner mic capture — moved off the deprecated main-thread ScriptProcessorNode to an AudioWorklet: no dropouts, no echo, and a cleaner signal into Whisper. (#43)
  • 🧠 whisper.cpp everywhere — the vendored whisper.cpp (whisperserver) is now the default STT engine in every context; the Python faster-whisper sidecar is mothballed (opt-in only). No Python required, ever, by default. (#46)
  • 🐾 Setup goes quiet when done — once API key + campaign + Roll20 are green, the Setup tab collapses to a "✓ You're all set" banner instead of nagging. (#67)
  • ⬆️ In-app speech-model upgrade — download small.en / medium.en for more accurate final transcription, two-tier with the built-in base.en. (#64)
  • 📋 Copy-Mod-to-clipboard — one-click copy of the Roll20 Mod for manual deploy (no browser automation needed). (#64)
  • 💎 Real app icon — the scrying-gem, replacing the invisible near-white octagon. (#60)

Fixes

  • roll_initiative accepts the model's stringified args ("true", JSON arrays) — no longer blocks combat start. (#44)
  • screenshot_roll20 timeout configurable (default 60 s) — survives heavy dynamic-lighting maps. (#56)
  • Stale ▸ ? · R1 combat band cleared on campaign switch / when no creature is current. (#54)
  • A/B clip saving (Save A/B clips in Config → Audio) now configurable + writes to the per-user data dir instead of failing into the read-only app bundle. (#71, #73)
  • CI: split build/publish so the parallel matrix can't race into two draft releases.

Downloads

Platform File
Windows DM-Whisper-0.1.2-Setup.exe
macOS (Apple Silicon) DM-Whisper-0.1.2-arm64.dmg
Linux DM-Whisper-0.1.2-x86_64.AppImage

You'll need: an Anthropic API key, a Roll20 campaign with the Mod installed (copy it from the Setup tab), and optionally a D&D Beyond account. The Setup tab walks you through it.

Known limitations

  • Unsigned builds · macOS Apple Silicon only · base.en bundled (larger models download in-app) · no auto-update yet.

🤖 Built + released via the project's CI/CD pipeline.

DM Whisper 0.1.0

Choose a tag to compare

@github-actions github-actions released this 22 Jun 20:39
5444090

DM Whisper 0.1.0 — first installable release

AI-assisted D&D 5e session management for Roll20 + D&D Beyond. This is the first packaged build: a one-click installer that runs the whole stack — no terminal, no Python, no Node, no Claude Code required. Speak to your table; the assistant tracks combat, rolls, conditions, and maps in Roll20 while you DM.

Heads-up: these builds are unsigned, so Windows SmartScreen and macOS Gatekeeper will warn on first launch (Win: More info → Run anyway; Mac: right-click → Open). Signing + notarization is the next release.


What's in the box

🔮 DM Whisper — the voice HUD. A transparent "scrying-gem" overlay that sits on top of Roll20. Push-to-talk → on-device speech-to-text → a Claude agent that drives your game. Includes a first-run setup familiar (API key + one-click Roll20 / D&D Beyond sign-in), a player inbox, a live combat HUD, and a training/after-action loop that learns your table's vocabulary.

⚔️ Live combat engine. HP, conditions, and initiative tracking; dice rolled through Roll20's own public roller (visible to players); turn hooks; area-of-effect resolution in a single call; and a tactical AI that scales monster cunning to its Int/Wis. PC HP is tracked separately so Beyond20 stays the source of truth for player sheets.

🗺️ Map prep suite. Battlemap upload, Claude-Vision wall detection, automatic dynamic-lighting walls/doors/windows, token creation, and spell zones — image in, playable lit map out.

📖 D&D Beyond integration (read-only). Pulls characters and monsters straight from your DDB account, including a monster mapper that normalizes the raw stat blocks into usable 5e data.


Under the hood

  • Browserless by default. Talks to Roll20 over its Firebase realtime database and to D&D Beyond via a short-lived token — no headless browser needed at runtime (Playwright remains an optional fallback).
  • On-device STT. Bundles a CPU whisper.cpp build with the base.en model as an offline floor; the familiar can download a larger/GPU model per your hardware.
  • Deterministic transcript correction. A post-STT layer (notation + literal + fuzzy/phonetic) fixes common D&D mishears without an LLM round-trip.
  • The assistant reports; you narrate. Numbers (HP/damage) stay in the DM-facing report; player-visible narration never leaks them.

Downloads

Platform File
Windows DM-Whisper-0.1.0-Setup.exe
macOS (Apple Silicon) DM-Whisper-0.1.0-arm64.dmg
Linux DM-Whisper-0.1.0-x86_64.AppImage

You'll need: an Anthropic API key, a Roll20 campaign with the assistant's Mod script deployed, and (optional) a D&D Beyond account for character/monster data. The setup familiar walks you through the sign-ins.

Known limitations

  • Unsigned builds (Gatekeeper/SmartScreen warnings).
  • macOS ships Apple Silicon only (no Intel build yet); Windows/Linux are x64.
  • base.en is the bundled STT model; larger/GPU models are familiar-downloaded on first run.

🤖 Built and released via the project's CI/CD pipeline (.github/workflows/release.yml).