A generative simulation kernel for living worlds. LLM-driven agents with persistent memory, validated by a deterministic engine, in a multi-region world with a labor-based economy.
Hallucination is a constraint problem. Give a generative system physics and it behaves like it lives in a universe. Remove the physics and it dreams.
An experimental simulation where autonomous LLM agents live in a persistent world and you watch — or intervene. It's not a game with a story. It's a substrate where stories emerge from scarcity, personality, and consequence.
The core insight: agents are stateless brains. Every tick they're handed a bounded view of their world, they propose an action, the engine validates and commits it, and consequences ripple forward. Over many ticks, roles consolidate, alliances form, economies stabilize or collapse, and the world accumulates history nobody scripted.
- LLM-driven agents — Every NPC makes decisions via the Claude/OpenAI/any-OpenRouter model you choose. They have traits, goals, memory, and permanent learned lessons.
- Labor-based economy — No free resources. Mines deplete. Crops must be harvested and replanted. Weapons break with use. Hunger escalates to starvation and death.
- Persistent multi-region world — Auto-saves every 10 ticks to disk. Two starter regions (Copper Hollow, Thornfield) connected by trade roads with multi-tick travel.
- Tiered memory system — Agents have rolling narrative memory (last 20 events) plus permanent "lessons" that never fade. Relationships remember their origin.
- Interaction zones — When two agents meet, time freezes and they exchange sub-ticks: conversations last 4 exchanges, combat up to 10, trades up to 4. The engine handles the back-and-forth.
- DM (Dungeon Master) layer — A second LLM watches the world and can spawn travelers, trigger raids, send caravans, discover new locations, or inject events when the world gets stale.
- Retry-with-correction — If an agent proposes an invalid action, the engine doesn't waste the tick. It sends a structured correction back and lets the LLM retry. Failed attempts become permanent lessons.
- Constraint learning — The engine tracks common failure patterns and surfaces them to future agents as "known constraints" in their prompts.
- SSE streaming GUI — Watch events flow in real-time through a browser. Reconnects gracefully.
- Node.js 18+
- An OpenRouter API key (or any OpenAI-compatible endpoint)
git clone https://github.com/your-username/mnehmos-engine.git
cd mnehmos-engine
npm install
# Set up your API key
cp .env.example .env
# Edit .env and add your OPENROUTER_API_KEY
# Start the GUI + engine
npm run gui:launch:devOpen http://127.0.0.1:4310/ in your browser. The simulation starts automatically and ticks every 45 seconds (LLM mode) or 4 seconds (rule-based fallback, no API key needed).
The engine runs fine without LLM access — it falls back to deterministic rule-based decisions. This is useful for testing the mechanics, debugging, or just watching the world tick at high speed.
# No .env needed, just run
npm run gui:launch:devYou start in Copper Hollow, a small village with seven NPCs:
- Watch Captain Mira — guards the village, investigates bandit activity
- Merchant Rynn — travels between towns, buys low and sells high
- Farmer Tomas — the food supply chain, growing wheat and vegetables
- Smith Brenna — forges weapons and tools, depends on iron from the mine
- Innkeeper Dalla — runs the Copper Mug, gathers gossip, feeds travelers
- Wanderer Kess — a drifter with a scarred past, looking for work
- Crow — a bandit hiding in the abandoned mine
A second region, Thornfield, sits to the east — a fortified market town with Guildmaster Sera, Sergeant Voss, and Guard Hask. They have more gold and connections but need Copper Hollow's iron and grain.
The DM may introduce more characters, locations, and crises over time.
┌─────────────────────────────────────────────────────────────┐
│ Browser GUI ◄── SSE stream ──── HTTP server (gui-cli.mjs) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ Engine tick loop (runtime.mjs) │
│ │
│ 1. Maintenance (time, hunger, │
│ durability) │
│ 2. Growth (DM proposals) │
│ 3. Decisions (LLM or rules) │
│ 4. Solo actions (move, gather, │
│ craft, etc.) │
│ 5. Zones (conversation, │
│ combat, trade) │
│ 6. Social (relationships, │
│ goal progress) │
│ 7. Observation (persist, emit) │
└──────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌───────────────────┐ ┌─────────────────────┐
│ LLM calls via │ │ Autosave → disk │
│ OpenRouter │ │ (apps/gui-web/ │
│ (llm-agent.mjs) │ │ saves/*.json) │
└───────────────────┘ └─────────────────────┘
See docs/ARCHITECTURE.md for a deep dive.
mnehmos-engine/
├── apps/gui-web/
│ ├── scripts/
│ │ ├── engine-runtime.mjs # Core simulation (tick loop, economy, zones)
│ │ ├── llm-agent.mjs # LLM integration (decisions, DM, retry loop)
│ │ └── gui-cli.mjs # HTTP server + SSE + embedded HTML GUI
│ ├── src/app/App.tsx # React GUI (alternative build target)
│ └── dist/ # Built GUI assets (generated)
├── src/ # Formal TypeScript specification (aspirational)
├── docs/ # Design documents and architecture notes
├── tests/ # Vitest test suites (for the formal spec)
├── .env.example # Environment template
└── package.json
This repository contains two layers:
- The runtime (
apps/gui-web/scripts/*.mjs) — three JavaScript files that actually run the simulation. This is what you interact with. - The formal spec (
src/anddocs/standalone_engine_*.md) — a TypeScript contract definition of the full architecture, including hard constraint validation, adjudication, and persistence layers that the runtime partially implements.
The runtime is a working prototype. The spec is the roadmap. Most features described in the spec are not yet in the runtime. See docs/GAP.md for what's implemented versus aspirational.
- Agents are stateless. Everything they need to reason, the engine must give them each tick. Memory is external.
- No free value. Resources are finite. Items break. Food runs out. Economy is a closed system.
- Death is permanent. Agents who fall stay fallen unless another agent actively revives them.
- Mistakes are lessons. A failed action gets a correction and a retry, then becomes a permanent lesson. Agents learn.
- The world remembers. Auto-save preserves every relationship, every scar, every debt across sessions.
- Let it breathe. The DM doesn't force drama every tick. Quiet periods are where alliances form and plans are made.
# Run the GUI host + webpack dev server
npm run gui:launch:dev
# Run just the backend (no browser auto-open)
npm run gui:host:dev
# Build production GUI bundle
npm run gui:web:build
# Run tests
npm run test:ontology:red
npm run test:gui:redAll configuration is via environment variables in .env. See .env.example for the full list.
The most important ones:
OPENROUTER_API_KEY— required for LLM modeMNEHMOS_ENGINE_NPC_MODEL— which model agents use (default:openai/gpt-5.4-nano)MNEHMOS_ENGINE_DM_MODEL— which model the DM uses (default: same as NPC)
The engine auto-saves to apps/gui-web/saves/autosave.json every 10 ticks. These files are:
- Plain JSON — human-readable, forkable, hackable
- Complete — every agent, cell, relationship, memory, lesson
- Gitignored — your world state is yours
To manually save/load:
# Save with a name
curl -X POST "http://127.0.0.1:4310/api/engine/save?name=before-raid"
# Load by name
curl -X POST "http://127.0.0.1:4310/api/engine/load?name=before-raid"
# List saves
curl "http://127.0.0.1:4310/api/engine/saves"See CONTRIBUTING.md.
MIT — see LICENSE.
Built on the thesis that coherent simulation requires bounded generation + deterministic validation + persistent consequences. Inspired by EVE Online's labor economy, the Dwarf Fortress emergent storytelling tradition, and the growing body of LLM-agent research.