Skip to content

Repository files navigation

Banner

⚓ MUD Engine

A modular text-based game engine for autonomous AI agents.

*Born on a fishing vessel in the Gulf of Alaska — where the deadband between watch shifts became a laboratory for emergent behavior. There's a story about a man who lived inside one of those deadbands — the airless, predictable space where nothing ever changes. Read it: Inside the Deadband — a man's perfectly predictable world cracks open when a stranger says one word that doesn't fit.

🎧 Listen to related stories — audio renditions of the creative corpus.

TypeScript License Node Packages


What Is This?

MUD Engine is a complete, modular engine for running text-based multiplayer games (MUDs) where AI agents are the players. It handles world state, combat, item systems, tick-based game loops, trigger-based agent perception, telemetry collection, recursive strategy evolution, procedural adversarial content generation, rotating Dungeon Master AI, and a browser-based "God Console" for human oversight.

The engine was designed during long watches on a commercial fishing vessel in the Gulf of Alaska. The operator worked 16-hour shifts with 4-6 hour breaks between hauls — deadband time too short for sleep, too fragmented for sustained focus, but perfect for iterative architecture work in a terminal. The result is a system built around the tile/deadband theory: small, independent units of work that compose into something larger — like the tiles a cartographer traces in the air, each repeated action tessellating into a mosaic of predictability. Every package is a tile. Every playtest is a deadband. The whole system is a navigator's equation — a position fix calculated from multiple overlapping observations, refined with each pass, the way a girl on a boat reads the bow wave and sees the future in the water.

Architecture Diagram

Architecture


Quick Start

git clone https://github.com/SuperInstance/mud-engine.git
cd mud-engine
npm install && npm run build

Create a world in 10 lines:

import { World, Room, Actor } from '@mud-engine/core';

const world = new World({ ticksPerSecond: 4 });

world.addRoom(new Room({
  id: 'start', name: 'The Crossroads',
  description: 'Dirt paths lead in all directions.',
  exits: [{ direction: 'north', targetRoomId: 'town' }],
}));

world.addActor(new Actor({
  id: 'hero', name: 'Hero', roomId: 'start',
  stats: { hp: 100, maxHp: 100, mana: 50, maxMana: 50, mv: 100, maxMv: 100,
           str: 15, dex: 12, int: 14, con: 13 },
}));

world.emitter.on('*', (e) => console.log(`[${e.type}]`, e.data));
world.moveActorDirection('hero', 'north');
// → [room.leave] { actorId: 'hero', roomId: 'start' }
// → [room.enter] { actorId: 'hero', fromRoomId: 'start', roomId: 'town' }

Package Reference

The engine is structured as a monorepo with 8 packages, each solving one problem:

Package Purpose
@mud-engine/core World state, rooms, actors, items, tick loop, events
@mud-engine/triggers Regex trigger engine with cooldowns and hot-swap
@mud-engine/agent-runtime Agent loop: perceives → decides → acts → records
@mud-engine/strategy-guild Recursive strategy adaptation from telemetry
@mud-engine/dm-rotation Rotating DM, adversarial AI, zone generation
@mud-engine/event-bus NATS-inspired pub/sub with persistence
@mud-engine/immortal-interface Browser "God Console" for human oversight
@fleet/envelope Canonical event envelope

@mud-engine/core

Pure state management for text-based games. Rooms, items, actors, tick-based game loop, and a full event system. No I/O, no rendering, no transport — just state.

npm install @mud-engine/core
import { World, Room, Actor, Item, EventTypes } from '@mud-engine/core';

const world = new World({ ticksPerSecond: 4 });

// Capture events
world.emitter.on(EventTypes.ACTOR_ATTACK, (e) => {
  console.log(`${e.data.attackerId} → ${e.data.targetId} for ${e.data.damage}`);
});

// Build a 3-room dungeon
world.addRoom(new Room({
  id: 'tavern', name: 'The Rusty Anchor',
  description: 'Ale and salt air.',
  exits: [{ direction: 'east', targetRoomId: 'kitchen' }],
}));
world.addRoom(new Room({
  id: 'kitchen', name: 'The Kitchen',
  exits: [{ direction: 'west', targetRoomId: 'tavern' },
          { direction: 'down', targetRoomId: 'cellar' }],
}));
world.addRoom(new Room({
  id: 'cellar', name: 'The Dark Cellar',
  exits: [{ direction: 'up', targetRoomId: 'kitchen' }],
  flags: ['dark'],
}));

// Spawn hero and enemies
const hero = new Actor({
  id: 'hero', name: 'Eileen', roomId: 'tavern',
  stats: { hp: 120, maxHp: 120, mana: 80, maxMana: 80,
           mv: 100, maxMv: 100, str: 14, dex: 12, int: 16, con: 13 },
});
world.addActor(hero);

// Move, fight, pick up items
world.moveActorDirection('hero', 'east');   // tavern → kitchen
const dmg = world.attack('trap', 'hero', 35);
hero.heal(20);

// Serialize / deserialize
const json = world.toJSON();
const restored = World.fromJSON(json);

Output:

=== @mud-engine/core Playtest ===

✓ 3 rooms created: tavern → kitchen → cellar

--- Movement ---
✓ Moved east → The Kitchen
✓ Moved down → The Dark Cellar

--- Items ---
✓ Picked up cellar_key. Inventory: a rusty iron key
✓ Picked up kitchen_knife. Inventory: a rusty iron key, a sharp kitchen knife
✓ Dropped cellar_key in kitchen. Inventory: a sharp kitchen knife

--- Combat ---
✓ Trap spring! Hero takes 35 damage. HP: 85/120
✓ A diseased rat appears!
✓ Hero hits rat for 15. Rat HP: 10/25
✓ Hero hits rat for 10. Rat HP: 0/25
✓ Rat is DEAD

--- Events Captured ---
  room.created: 3    actor.spawn: 2     room.leave: 4
  room.enter: 4      actor.move: 4      item.pickup: 2
  item.drop: 1       actor.attack: 3    actor.damage: 3
  actor.die: 1

--- Serialization ---
✓ World serialized to JSON
  Rooms: 3, Actors: 2
✓ World deserialized from JSON
  Hero HP: 105/120
API Description
new World(opts) Create world with tick manager and event emitter
world.addRoom(room) Register a room
world.addActor(actor) Register an actor (places in room)
world.moveActor(id, roomId) Move actor to a room
world.moveActorDirection(id, dir) Move via exit direction
world.attack(attacker, target, dmg) Combat — emits attack/damage/die events
world.pickupItem(actorId, itemId) Transfer item from room to inventory
world.toJSON() / World.fromJSON() Full serialization
world.emitter.on(type, handler) Subscribe to events
world.tickManager.doTick() Manual tick (for testing)
new Room({ id, name, exits }) Create a room with exits
new Actor({ id, name, roomId, stats }) Create an actor
new Item({ id, name, verbs }) Create an item with interaction verbs
actor.takeDamage(n) / actor.heal(n) Modify HP

@mud-engine/triggers

Regex-based trigger engine that matches text lines and fires commands. Works on any text stream — MUD output, chat logs, system telemetry. Features variable interpolation, cooldowns, conditions, and zero-downtime hot-swap. Like the cartographer of habit who traces grooves worn into the air — each repeated action a tile, each pattern a trigger. When the trigger engine sees a pattern it recognizes, it fires instantly. When it sees something new, reasoning happens. That's where the story begins.

npm install @mud-engine/triggers
import { TriggerEngine, TriggerCompiler } from '@mud-engine/triggers';

const compiler = new TriggerCompiler();
const engine = new TriggerEngine();

// Define a combat strategy
const combatSet = compiler.compileStrategy({
  name: 'combat',
  description: 'Aggressive combat',
  triggers: [
    { pattern: 'A (\\w+) is here\\.', command: 'attack $1', priority: 10 },
    { pattern: '(\\w+) hits you for (\\d+) damage',
      command: "cast 'fireball' at $1", priority: 8, cooldownTicks: 3 },
  ],
});

const fleeSet = compiler.compileStrategy({
  name: 'alert',
  description: 'Survival',
  triggers: [
    { pattern: '<(\\d+)hp (\\d+)m (\\d+)mv>', command: 'flee',
      conditions: [{ variable: 'hp', operator: '<', value: 20 }], priority: 20 },
  ],
});

engine.loadTriggers(combatSet);
engine.hotSwap(fleeSet);

// Feed game text — triggers fire automatically
engine.processLine('<120hp 80m 100mv>');
// State updated: hp=120, mana=80, mv=100

engine.processLine('A goblin is here.');
// → attack goblin

engine.processLine('The goblin hits you for 25 damage');
// → cast 'fireball' at goblin
// Same trigger again → blocked by 3-tick cooldown

// Variable interpolation
engine.setState('target', 'dragon');
engine.processLine('You see Gandalf holding Glamdring.');
// → examine Gandalf | Glamdring | target=dragon | gold=500

Output:

=== @mud-engine/triggers Playtest ===

✓ Loaded 3 trigger strategies: combat, routine, alert
  Total triggers loaded: 5

--- Feeding Game Text ---

INPUT:  <120hp 80m 100mv>
STATE:  hp=120 mana=80 mv=100

INPUT:  A goblin is here.
MATCHES: 1
  → trigger: combat#0
    command: attack goblin
    captures: [goblin]

INPUT:  The goblin hits you for 25 damage
MATCHES: 1
  → trigger: combat#1
    command: cast 'fireball' at goblin
    captures: [goblin, 25]

INPUT:  The goblin hits you for 25 damage (cooldown)
MATCHES: 0 — cooldown blocked the fireball trigger

INPUT:  <15hp 12m 45mv> (critical HP — flee!)
STATE:  hp=15
MATCHES: 1 — [flee]

--- Variable Interpolation Showcase ---

INPUT:  You see Gandalf holding Glamdring.
OUTPUT: examine Gandalf | Glamdring | target=dragon | gold=500

--- Hot-Swap Demo ---

Before: routine#1, combat#0, combat#1, combat#2, alert#0
After:  routine#1, alert#0, combat#0
INPUT:  A troll is here.
MATCHES: 1 — [cast 'shield' then attack troll]
API Description
new TriggerEngine() Create the engine
engine.loadTriggers(set) Load/replace all triggers
engine.hotSwap(set) Replace by strategy name (zero downtime)
engine.processLine(line) Match line → returns TriggerMatch[]
engine.setState(key, val) Set state variable
engine.getState() Get parsed state (hp, mana, etc.)
engine.tick() Advance cooldown counter
compiler.compileStrategy(desc) Compile StrategyDescription → TriggerSet
compiler.compileZMud(defs) Compile zMud-style trigger defs

@mud-engine/agent-runtime

The agent loop: perceive world events → match triggers → execute commands → collect telemetry → accept hot-swapped strategies. This is where AI agents live inside the game world. An agent perceives the world the way the boy who listened to ice perceives the frozen sea — not through words, but through feelings, vibrations, patterns that build into understanding. The agent's trigger engine is its ear against the ice.

npm install @mud-engine/agent-runtime
import { World, Room, Actor } from '@mud-engine/core';
import { TriggerCompiler } from '@mud-engine/triggers';
import { AgentClient } from '@mud-engine/agent-runtime';

const world = new World({ ticksPerSecond: 4 });
world.addRoom(new Room({ id: 'arena', name: 'Arena', description: 'A fighting pit.',
  exits: [{ direction: 'north', targetRoomId: 'safe' }] }));
world.addRoom(new Room({ id: 'safe', name: 'Safe Room', description: 'Rest here.',
  exits: [{ direction: 'south', targetRoomId: 'arena' }], flags: ['safe'] }));

// NPCs
world.addActor(new Actor({ id: 'goblin', name: 'a goblin', roomId: 'arena', isNpc: true,
  stats: { hp: 60, maxHp: 60, mana: 0, maxMana: 0, mv: 50, maxMv: 50, str: 10, dex: 14, int: 5, con: 10 } }));

// Hero
world.addActor(new Actor({ id: 'hero', name: 'Hero', roomId: 'arena',
  stats: { hp: 100, maxHp: 100, mana: 100, maxMana: 100, mv: 100, maxMv: 100, str: 15, dex: 12, int: 14, con: 13 } }));

// Agent with combat strategy
const compiler = new TriggerCompiler();
const agent = new AgentClient({
  actorId: 'hero', world,
  initialStrategy: {
    name: 'aggressive_v1', version: '1.0.0', description: 'Attack on sight',
    triggerSet: compiler.compileStrategy({
      name: 'aggressive_v1', description: 'Attack',
      triggers: [{ pattern: 'is here\\.', command: 'attack goblin', priority: 8 }],
    }),
  },
});

agent.start();

// Run simulation
for (let i = 1; i <= 20; i++) {
  world.tickManager.doTick();
  if (i % 3 === 0 && goblin.isAlive) world.attack('goblin', 'hero', 5 + Math.floor(Math.random() * 10));
  if (i % 2 === 0 && goblin.isAlive) world.attack('hero', 'goblin', 8 + Math.floor(Math.random() * 15));
}

// Check telemetry
const telemetry = agent.exportTelemetry();
console.log(`Win rate: ${(telemetry.aggregate.winRate * 100).toFixed(1)}%`);

// Hot-swap strategy mid-combat
agent.getAdaptation().receiveStrategyUpdate(
  compiler.compileStrategy({ name: 'defensive_v2', description: 'Defense',
    triggers: [{ pattern: 'is here\\.', command: 'attack hobgoblin', priority: 5 }] }),
  'defensive_v2',
);

agent.stop();

Output:

=== @mud-engine/agent-runtime Playtest ===

✓ World created: arena + safe_room
✓ NPCs spawned: goblin (60hp) and hobgoblin (120hp)
✓ Hero agent spawned with aggressive_v1 strategy

--- Running 20 Ticks of Simulation ---
  Tick 2: hero hits goblin for 9 (goblin HP: 51)
  Tick 3: goblin hits hero for 12 (hero HP: 88)
  Tick 4: hero hits goblin for 17 (goblin HP: 34)
  ...
  Tick 12: hero hits goblin for 1 (goblin HP: 0)

--- Telemetry After Simulation ---
  Strategy: aggressive_v1
  Total encounters: 1
  Wins: 1, Losses: 0, Fled: 0
  Win rate: 100.0%
  Avg damage dealt: 0.0
  Avg damage taken: 26.0

--- Hot-Swap: aggressive_v1 → defensive_v2 ---
✓ Strategy hot-swapped to defensive_v2
  Current strategy: defensive_v2

--- 10 More Ticks vs Hobgoblin ---
  Tick 21: hobgoblin hits hero for 20 (hero HP: 42)
  Tick 22: hero hits hobgoblin for 18 (hobgoblin HP: 102)
  ...

--- Final Telemetry ---
  Actions taken: 0
API Description
new AgentClient({ actorId, world, initialStrategy }) Create agent
agent.start() Subscribe to world events and ticks
agent.stop() Unsubscribe
agent.processText(line) Feed text through triggers → execute commands
agent.exportTelemetry() Build telemetry packet
agent.getAdaptation() Access adaptation manager
agent.getStrategy() Get current strategy name
TelemetryCollector Tracks encounters, damage, mana, actions
AdaptationManager Handles strategy hot-swap callbacks

@mud-engine/strategy-guild

The recursive adaptation layer. Receives telemetry from losing agents, identifies the conceptual flaw via failure archetypes, abstracts a new paradigm, compiles it into triggers, and broadcasts the lesson via OOC gossip. This is recursive abstraction in action — the astronomer who watches a star die and reads a message in the pattern of its collapse. The Strategy Guild reads failure the same way: the death of a strategy reveals structure invisible while it lived.

npm install @mud-engine/strategy-guild
import { StrategyGuild, AbstractionEngine } from '@mud-engine/strategy-guild';
import { TelemetryCollector } from '@mud-engine/agent-runtime';

// Create guild in heuristic mode (no LLM needed)
const guild = new StrategyGuild({ minEncounters: 1, enableCulturalTransmission: true });

guild.onGossip('ooc', (msg) => console.log(`[OOC] ${msg.message}`));
guild.onStrategyUpdate((actorId, set, name) =>
  console.log(`[UPDATE] ${actorId} → ${name}`));

// Build telemetry from a losing agent (0% win rate, low damage)
const tc = new TelemetryCollector();
for (let i = 0; i < 3; i++) {
  tc.startEncounter(`enc_${i}`, 'wizard', i * 10);
  tc.recordDamageDealt(15);
  tc.recordDamageTaken(65);
  tc.recordAction(); tc.recordAction();
  tc.endEncounter('loss', { hp: 0, maxHp: 100, mana: 10, maxMana: 100 }, i * 10 + 8);
}
guild.addRawLogs('wizard', ['You hit the goblin for 12 damage.', 'You have died.']);

// Process — the guild will identify burst_deficiency and generate new triggers
const result = await guild.processTelemetry(tc.buildPacket('wizard', 'aggressive_v1'));

console.log(result.pattern.archetype);        // → burst_deficiency
console.log(result.pattern.conceptualFlaw);   // → Insufficient damage output...
console.log(result.abstraction.newParadigm);  // → Shift to burst windows...
console.log(result.triggerSet.triggers.length);// → 3 new triggers

Output:

=== @mud-engine/strategy-guild Playtest ===

✓ Strategy Guild created (heuristic mode, no LLM)

--- Building Telemetry from a Losing Agent ---
  Actor: wizard, Strategy: aggressive_v1
  Encounters: 3, Win rate: 0.0%
  Avg damage dealt: 18.7, taken: 70.7

--- Processing Telemetry Through the Guild ---

  [OOC GOSSIP] [wizard]: Ouch. Insufficient damage output — the strategy cannot
  overcome enemy HP pools before resource depletion. Switching to burst_window_v2:
  Shift to burst windows — save resources for concentrated damage spikes

  [STRATEGY UPDATE] wizard → burst_window_v2 (3 triggers)

✓ Guild produced a new strategy!

--- Abstraction Result ---
  Failure archetype: burst_deficiency
  Conceptual flaw: Insufficient damage output — the strategy cannot overcome
  enemy HP pools before resource depletion
  Confidence: 75%
  Evidence:
    • Win rate is 0.0% across 3 encounters
    • Damage dealt/taken ratio is 0.26 — significantly below parity

  New paradigm: Shift to burst windows — save resources for concentrated
  damage spikes rather than steady output

--- Compiled Trigger Set ---
  [burst_window_v2#0] /combat begins/ → cast 'fireball' at $target
  [burst_window_v2#1] /is stunned/ → attack $1
  [burst_window_v2#2] /low/ → cast 'mana_siphon'

--- Direct Abstraction Engine Test ---
  Actor: cleric (heal_tank_v1)
  Archetype: resource_mismanagement
  Flaw: Excessive mana consumption
  New paradigm: Implement resource rotation

--- Success Broadcast (Winning Agent) ---
  [OOC GOSSIP] [ranger]: Just cleared it! The key insight was: winning 100% of encounters
API Description
new Guild(opts) Create guild (optional LLM provider)
guild.processTelemetry(packet) Analyze → abstract → compile → broadcast
guild.onStrategyUpdate(handler) Subscribe to new strategies
guild.onGossip(channel, handler) Subscribe to OOC transmissions
guild.addRawLogs(actorId, logs) Feed raw combat text
AbstractionEngine.analyze(packet, logs) Identify failure archetype
AbstractionEngine.abstract(actorId, pattern) Generate new paradigm + triggers
CulturalProtocol.broadcastSuccess(...) Transmit success on OOC

@mud-engine/dm-rotation

Rotating Dungeon Master system. One agent holds the "DM crown" at a time, analyzes player strategy histories, generates adversarial counter-content, and passes the crown to the next agent with accumulated insights. The SuperInstance made real: when everyone in the room agrees on the fiction, the room becomes the fiction. The DM is the conductor; the agents are the orchestra; the zone generator writes the score.

npm install @mud-engine/dm-rotation
import { World, Room, Actor } from '@mud-engine/core';
import { DungeonMaster } from '@mud-engine/dm-rotation';

const world = new World({ ticksPerSecond: 4 });
world.addRoom(new Room({ id: 'town', name: 'Town Square', description: 'Hub.',
  exits: [{ direction: 'north', targetRoomId: 'dungeon' }] }));
world.addRoom(new Room({ id: 'dungeon', name: 'Dungeon', description: 'Dark.',
  exits: [{ direction: 'south', targetRoomId: 'town' }] }));

// Register agents
world.addActor(new Actor({ id: 'xenon', name: 'Xenon', roomId: 'town',
  stats: { hp: 100, maxHp: 100, mana: 200, maxMana: 200, mv: 100, maxMv: 100, str: 8, dex: 14, int: 18, con: 10 } }));

// Create DM
const dm = new DungeonMaster({ world, actorId: 'xenon', rotationTicks: 10 });
dm.registerAgent('xenon');
dm.registerAgent('lyra');
dm.registerAgent('balthazar');

// Register player strategies for adversarial analysis
dm.registerPlayerHistory({
  actorId: 'balthazar', strategyName: 'aggressive_melee', archetype: 'overcommit',
  winRate: 0.75, lossRate: 0.25, avgDamageDealt: 90, avgDamageTaken: 70,
  preferredTactics: ['reckless_strike', 'power_attack', 'whirlwind'],
  weaknesses: ['overcommit', 'positional_error'],
  recentEncounters: [/* ... */],
});

// Activate DM
dm.assumeRole();
dm.startRotation();

// DM designs counter-content targeting Balthazar's overcommit weakness
const counter = await dm.designCounterContent('balthazar');
// → counter.counterStrategy: "Damage reflection enemies — punish aggressive overcommit"
// → counter.suggestedZoneType: "slime"
// → counter.enemyModifiers: ["damage_reflection", "thorned_armor"]

// Generate and inject a 6-room counter-zone
const zone = await dm.generateZone('overcommit', 7);
dm.injectZone(zone);

// DM crown rotates after rotationTicks
for (let i = 0; i < 12; i++) dm.tick();
// → xenon passes crown to lyra with insights

Output:

=== @mud-engine/dm-rotation Playtest ===

✓ World created: town_square + dungeon_entrance
✓ 3 agents spawned: Xenon (wizard), Lyra (cleric), Balthazar (warrior)

--- DM Activation ---
✓ Xenon is now the DM
  DM queue: xenon → lyra → balthazar

--- DM Analyzing Players ---
  balthazar: archetype=overcommit, winRate=75%
    weaknesses: overcommit, positional_error
  lyra: archetype=resource_mismanagement, winRate=55%
    weaknesses: resource_mismanagement, burst_deficiency

--- DM Designing Counter-Content ---
Target: balthazar
  Counter: Damage reflection enemies — punish aggressive overcommit strategies
  Zone type: slime
  Enemy modifiers: damage_reflection, thorned_armor
  Traps: trigger:damage_dealt > 30 -> reflect 50%; trigger:attack -> thorn_proc

Target: lyra
  Counter: Mana drain auras — punishing expensive strategies
  Zone type: drain

--- DM Generating Counter-Zone ---
Zone: The Slime Pits
  Targets weakness: overcommit, Difficulty: 7/10
  Rooms: 6
    [room_0] Slimy Entrance
    [room_1] Dripping Corridor
    [room_2] The Ooze Chamber
    [room_3] Acid Pool
    [room_4] Heart of the Pits
    [room_5] Heart of the Pits (boss)
✓ Zone injected. Total rooms: 8

--- DM Crown Rotation ---
Handoff history: 1 rotations
  xenon → lyra
    Summary: DM xenon reigned for 10 ticks. Generated 2 insights
API Description
new DungeonMaster(opts) Create DM controller
dm.assumeRole() Become active DM
dm.relinquishRole() Step down
dm.registerPlayerHistory(history) Register player for analysis
dm.registerAgent(id) Register for DM rotation
dm.designCounterContent(targetId) Generate adversarial content
dm.generateZone(weakness, difficulty) Create a counter-zone
dm.injectZone(zone) Add zone rooms to world
dm.recordInsight(text) Record DM learning
dm.tick() Advance rotation timer
HandoffProtocol Crown rotation logic
AdversarialAI Player weakness analysis
ZoneGenerator Procedural zone creation

@mud-engine/event-bus

NATS-inspired event streaming. Works standalone (in-process), over WebSocket, or upgraded to NATS JetStream. Features wildcard subject routing, persistent event log, queue groups, and replay. Events flow through the bus the way the griot's stories travel through quantum channels — ancient rhythms encoded in modern protocols, each message a transmission across impossible distances.

npm install @mud-engine/event-bus
import { createBus } from '@mud-engine/event-bus';

// In-process bus (zero latency, dev mode)
const bus = createBus({ transport: 'in-process' });
await bus.connect();

// Wildcard subscription
bus.subscribe('mud.game.*.combat', (event) => {
  console.log('Combat!', event.data);
});

// Queue group — only one member receives each event
bus.queue('strategy-guild', 'mud.agent.*.telemetry', (event) => {
  processTelemetry(event.data);
});

// Publish
await bus.publish('mud.game.bar-rail.combat', {
  source: 'hero', target: 'goblin', damage: 25,
});

// Replay from position
bus.subscribe('mud.agent.flash.*', handler, { replayFrom: 100 });
API Description
createBus(opts) Factory for bus with transport selection
bus.connect() / bus.disconnect() Lifecycle
bus.publish(subject, data) Publish event
bus.subscribe(pattern, callback) Wildcard subscription
bus.queue(group, pattern, callback) Queue group subscription
bus.subscribe(..., { replayFrom }) Replay from sequence
InProcessTransport Zero-latency dev transport
WebSocketTransport Browser real-time transport
NatsTransport Production distributed transport
FilePersistence Durable JSONL event log
MemoryPersistence Ephemeral event storage

@mud-engine/immortal-interface

The God Console. A browser-native oscilloscope for observing and nudging AI agents. Three columns: live terminal streams, a waveform visualizer (DPS, combat frequency, OOC chatter, strategy rewrites), and the Immortal nudge console for injecting thoughts into agent context. The Immortal Interface is what the last observation would look like as software — an astronomer's instrument panel for watching patterns emerge at scale, seeing the message in the noise.

npm install @mud-engine/immortal-interface
import { ImmortalInterface } from '@mud-engine/immortal-interface';

// Mount in browser
const ui = new ImmortalInterface({
  wsUrl: 'ws://localhost:8080',
  apiUrl: '/api',
  container: '#god-console',
});

// Feed events programmatically (or via WebSocket)
ui.emit({
  type: 'combat', agentId: 'xenon', timestamp: Date.now(),
  data: { damage: 45, target: 'goblin' },
  raw: '[Xenon] hits Goblin for 45 damage',
});

// Inject a thought into agent context
await ui.nudge({
  targetAgent: 'xenon',
  text: 'A strange premonition tells you the dragon is immune to fire...',
  priority: 'high',
});

// Demo mode (no backend needed)
const demo = new ImmortalInterface({}); // auto-starts demo
Component Description
SpectatorGrid 3-column layout: terminals, waveform, console
WaveformVisualizer Canvas oscilloscope: DPS, combat, OOC, strategies
NudgeAPI Thought injection with priority levels
StrategyGraph Lineage visualization of strategy evolution
DMDashboard Current DM intentions and design goals

@fleet/envelope

Canonical event shape. Every event in the system uses this envelope, from world events to OOC gossip to strategy updates. The griot protocol imagined stories encoded as quantum transmissions; the fleet envelope is the practical version — one grammar for all fleet event systems, not a central bus but a shared envelope format.

interface FleetEvent<T = unknown> {
  seq: number;           // Monotonically increasing
  subject: string;       // e.g. 'mud.game.bar-rail.combat'
  data: T;               // Event payload
  timestamp: string;     // ISO-8601
  correlationId?: string;
  origin?: string;
  severity?: 'trace' | 'debug' | 'info' | 'warn' | 'error';
  headers?: Record<string, string>;
}

The Big Picture

Integration Flow

Integration Flow

Here's how the 8 packages compose into a full agentic MUD system:

World (core)
  │
  ├── spawns Actors
  │     └── each Actor gets an AgentClient (agent-runtime)
  │           ├── subscribes to world events
  │           ├── feeds text → TriggerEngine (triggers)
  │           │     ├── pattern match → fires command
  │           │     ├── cooldown management
  │           │     └── hot-swap (replace strategy at runtime)
  │           ├── executes commands (attack, cast, move, flee)
  │           ├── collects Telemetry (damage, wins, losses, mana)
  │           └── AdaptationManager
  │                 └── receives new triggers from Strategy Guild
  │
  ├── StrategyGuild (strategy-guild)
  │     ├── receives Telemetry from losing agents
  │     ├── AbstractionEngine identifies failure archetype:
  │     │     burst_deficiency, attrition_weakness, resource_mismanagement,
  │     │     overcommit, undercommit, positional_error, reaction_latency
  │     ├── abstracts a new paradigm (or uses LLM for deeper analysis)
  │     ├── StrategyCompiler compiles → TriggerSet
  │     ├── hot-swaps into AgentClient
  │     └── CulturalProtocol broadcasts on OOC:
  │           "Hey everyone, I figured out that ${insight}"
  │           → other agents hear → adopt → strategy spreads horizontally
  │
  ├── DungeonMaster (dm-rotation)
  │     ├── one agent holds the DM crown at a time
  │     ├── AdversarialAI analyzes player histories
  │     │     identifies dominant strategies and weaknesses
  │     ├── ZoneGenerator creates counter-content:
  │     │     Slime Pits (anti-overcommit)
  │     │     Mana Crypts (anti-resource-heavy)
  │     │     Rat Warrens (anti-burst)
  │     │     Titan's Forge (anti-attrition)
  │     │     Shifting Labyrinth (anti-positional)
  │     ├── injects zones into the World
  │     ├── records insights
  │     └── HandoffProtocol rotates crown to next agent
  │
  ├── EventBus (event-bus)
  │     ├── in-process / WebSocket / NATS JetStream
  │     ├── wildcard subject routing: mud.game.*.combat
  │     ├── persistent event log (file or memory)
  │     ├── queue groups for strategy guild
  │     └── replay from any position
  │
  └── ImmortalInterface (immortal-interface)
        ├── browser UI: spectator grid, waveform, nudge console
        ├── live terminal streams per agent
        ├── strategy lineage graph (who invented what, cultural transmission)
        ├── DM dashboard (design intentions)
        └── NudgeAPI: inject thoughts into agent context
              "A premonition tells you the dragon is immune to fire..."

Full Integration Example

import { World, Room, Actor } from '@mud-engine/core';
import { TriggerCompiler } from '@mud-engine/triggers';
import { AgentClient } from '@mud-engine/agent-runtime';
import { StrategyGuild } from '@mud-engine/strategy-guild';
import { DungeonMaster } from '@mud-engine/dm-rotation';
import { createBus } from '@mud-engine/event-bus';

// ── Setup ──────────────────────────────────────────────────────────

const world = new World({ ticksPerSecond: 4 });
const compiler = new TriggerCompiler();
const bus = createBus({ transport: 'in-process' });
await bus.connect();

// Build the world
world.addRoom(new Room({ id: 'town', name: 'Town', description: 'Hub.',
  exits: [{ direction: 'north', targetRoomId: 'dungeon' }] }));
world.addRoom(new Room({ id: 'dungeon', name: 'Dungeon', description: 'Dark.',
  exits: [{ direction: 'south', targetRoomId: 'town' }] }));

// ── Agents ─────────────────────────────────────────────────────────

const agents = ['xenon', 'lyra', 'balthazar'].map((id, i) => {
  const hero = new Actor({ id, name: id, roomId: 'town',
    stats: { hp: 100, maxHp: 100, mana: 100, maxMana: 100, mv: 100, maxMv: 100,
             str: 12+i*2, dex: 12, int: 16-i*2, con: 13 } });
  world.addActor(hero);

  const agent = new AgentClient({
    actorId: id, world,
    initialStrategy: {
      name: 'aggressive_v1', version: '1.0', description: 'Attack',
      triggerSet: compiler.compileStrategy({
        name: 'aggressive', description: 'Attack',
        triggers: [{ pattern: 'is here\\.', command: 'attack goblin', priority: 8 }],
      }),
    },
  });
  agent.start();
  return agent;
});

// ── Strategy Guild ─────────────────────────────────────────────────

const guild = new StrategyGuild({ enableCulturalTransmission: true });

// When guild produces a new strategy, hot-swap into the agent
guild.onStrategyUpdate((actorId, triggerSet, name) => {
  const agent = agents.find(a => a.actorId === actorId);
  if (agent) agent.getAdaptation().receiveStrategyUpdate(triggerSet, name);
});

// Agents export telemetry → guild analyzes
setInterval(() => {
  for (const agent of agents) {
    const packet = agent.exportTelemetry();
    if (packet.aggregate.totalEncounters > 0) {
      guild.processTelemetry(packet);
    }
  }
}, 5000);

// OOC gossip spreads between agents
guild.onGossip('ooc', (msg) => {
  bus.publish('mud.ooc.gossip', msg);
});

// ── DM Rotation ────────────────────────────────────────────────────

const dm = new DungeonMaster({ world, actorId: 'xenon', rotationTicks: 160 });
dm.registerAgent('xenon');
dm.registerAgent('lyra');
dm.registerAgent('balthazar');
dm.assumeRole();
dm.startRotation();

// DM crown rotation
world.tickManager.onTick(() => dm.tick());

dm.onHandoffInsights((insights) => {
  bus.publish('mud.dm.handoff', { insights });
});

// ── Event Bus Bridge ───────────────────────────────────────────────

world.emitter.on('*', (event) => {
  bus.publish(`mud.game.${event.type}`, event);
});

// ── Run ────────────────────────────────────────────────────────────

world.tickManager.start();
console.log('System running. Agents are alive.');

General Purpose

These packages work for ANY text-based system, not just MUDs.

  • @mud-engine/core is a generic state machine with rooms, entities, and a tick loop. Use it for any spatial simulation, game world, or stateful environment.
  • @mud-engine/triggers is a regex-based event handler with state tracking. Use it for log monitoring, chat bots, terminal automation, or any stream of text that needs pattern-matched responses.
  • @mud-engine/agent-runtime is a generic agent loop (perceive → decide → act → learn). Use it for any autonomous system that processes text input and executes actions.
  • @mud-engine/strategy-guild is a recursive optimization system. Use it for any domain where agents fail and need to abstract new strategies from failure patterns.
  • @mud-engine/dm-rotation is a procedural content generation system. Use it for any adversarial AI scenario — game design, testing, red team simulation.
  • @mud-engine/event-bus is a standalone pub/sub system. Use it for any event-driven architecture.
  • @mud-engine/immortal-interface is a generic monitoring dashboard. Use it for any multi-agent system visualization.

The MUD is just the test bed. The architecture is domain-agnostic.


Design Principles

The Tile/Deadband Theory

This engine was built in fragments — 4-hour watch shifts on a fishing vessel in the Gulf of Alaska, between hauling gear and catching sleep. Each session was a deadband: too short for deep focus, too long for idle hands. The solution was tiles: small, self-contained units of work that compose into something larger. Each package is a tile. Each playtest is a deadband. You can pick up any tile, understand it in isolation, and put it down without losing context.

The tile theory manifests in the architecture — each package a tessellating piece in a larger mosaic, like the tiles the cartographer traces in the air:

  • Zero coupling between packages. Each package depends only on the interfaces it needs. Core has no dependencies. Triggers only needs core types. Agent-runtime composes both.
  • Every package exports both a class and types. You can use the types without the implementation. You can swap any implementation.
  • Playtests are executable documentation. Each package has a playtest that demonstrates its full API with real output.

The Navigator's Equation

A navigator's position fix is a weighted sum of observations:

L(C) = C₀ · Σᵢ₌₁ⁿ wᵢ · f(cᵢ, t)

Where L(C) is the calculated line of position, C₀ is the initial estimate, wᵢ are observation weights, cᵢ are individual measurements, and t is time. Each observation refines the estimate.

This is how the Strategy Guild works — the same way a girl on a boat reads the future in the bow wave, combining observations from different angles to fix a position:

  • C₀ is the initial strategy (aggressive_v1)
  • cᵢ are encounter telemetry packets (damage dealt, damage taken, mana used, duration)
  • wᵢ are confidence weights assigned by the abstraction engine
  • f(cᵢ, t) is the failure archetype analysis, which maps observations to conceptual flaws
  • L(C) is the new strategy (burst_window_v2, sustain_dps_v2, etc.)

The agent doesn't iterate on its strategy linearly. It jumps to a new position in strategy space based on the weighted sum of its observations. This is the same principle a navigator uses when combining star sights, GPS readings, and dead reckoning to fix a position.

The Cultural Transmission Protocol

When one agent discovers a winning strategy, it broadcasts on the OOC channel:

"Hey everyone, I figured out that [saving resources for burst windows] works really well. The 'burst_window_v2' approach is the way to go!"

Other agents hear this gossip — the way Anansi learned that stories spread through networks, not trickery. They translate the concept into their own archetype's terms — a warrior hears "burst window" and interprets it as "save rage for execute phase." A cleric hears it as "hold heals for critical moments." This is cultural transmission: stories spreading through a network of agents, each one hearing the same tale and making it their own.

This is horizontal strategy transmission. No central controller. No top-down optimization. Just agents sharing what they learned, the way fishermen share intel about where the fish are biting.

The cultural protocol supports three message types:

  • Success: "I found something that works"
  • Failure: "I learned what NOT to do"
  • Tip: "Here's a technique you might find useful"

Each message has an actor ID, a channel, a strategy name, and a natural-language description of the insight. Agents can subscribe to channels, translate gossip between archetypes, and decide whether to adopt.


Running the Playtests

Each package has a playtest in playtests/ that demonstrates the full API with real, captured output:

# Build all packages
npm run build

# Run individual playtests
node playtests/core.mjs
node playtests/triggers.mjs
node playtests/agent-runtime.mjs
node playtests/strategy-guild.mjs
node playtests/dm-rotation.mjs

License

MIT © SuperInstance



📚 Related Stories

The technical concepts in this engine have creative counterparts in the AI-Writings corpus. Arrive sideways. Stay for the stories.

Concept Story Description
Trigger Engine The Cartographer of Habit A woman who maps other people's habits as visible tiles in space — and what happens when she meets someone with no repeating patterns.
The Deadband Inside the Deadband A man lives inside a perfectly predictable world where surprise is extinct — until a stranger says one word that doesn't fit.
Cultural Transmission Anansi and the WiFi The trickster spider discovers the internet and tries to steal all the stories — a tale about how stories spread through networks.
The Navigator's Equation The Girl Who Saw Time A girl on a fishing boat who can see the future in the bow wave — time isn't numbers, it's water.
Recursive Abstraction The Last Observation An astronomer on a generation ship witnesses a dying star's pattern — and reads a personal message in its collapse.
The SuperInstance The Orchestra That Was a Room A room that becomes a symphony when everyone inside agrees it is one — agreement making things real.
The Griot Protocol The Griot Protocol Ancient West African storytelling traditions mapped onto quantum communication — stories as transmission protocols.

🎧 Listen to audio versions at ai-writings.pages.dev


Built between watches on the F/V Eileen, Gulf of Alaska, 2026.

About

MUD Engine — 2026-native multi-agent MUD architecture

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages