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.
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.
git clone https://github.com/SuperInstance/mud-engine.git
cd mud-engine
npm install && npm run buildCreate 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' }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 |
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/coreimport { 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 |
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/triggersimport { 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=500Output:
=== @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 |
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-runtimeimport { 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 |
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-guildimport { 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 triggersOutput:
=== @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 |
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-rotationimport { 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 insightsOutput:
=== @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 |
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-busimport { 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 |
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-interfaceimport { 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 |
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>;
}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..."
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.');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.
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.
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.
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.
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.mjsMIT © SuperInstance
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.