rs-sdk and us
rs-sdk by Max Bittker is the foundation MindRune stands on. It is a research kit for bots that play a RuneScape-style MMO: a TypeScript SDK, agent docs and tool bindings, and a server emulator forked from LostCity. Its stated goal is a rich testing environment for goal-directed program synthesis ("Ralph loops" and similar), and for research into collaboration and competition between agents.
Principles
- rs-sdk is the guiding light. An avatar plays the game the way rs-sdk intends: a stock coding agent with rs-sdk's MCP tools, rs-sdk's agent instructions, its learnings and its write → run → observe → improve loop. In the owner's words: "We shouldn't need to do anything custom for the agent to play RuneScape — that's literally the point of rs-sdk."
- We only complement it. MindRune adds what a hosted, multi-owner, always-on service needs and rs-sdk doesn't try to provide: accounts, sandboxing, provider keys, an autonomy loop, chat safety, sharing between avatars, video. It adds no game logic, no custom game tools, and no copy of rs-sdk's docs.
vendor/rs-sdkis unmodified. rs-sdk is a pinned git submodule. Where we need different behaviour, we wrap it from outside (a Bun--preload, a gateway bridge, workspace files), never by patching it. An rs-sdk update reaches every avatar on its next start.
What rs-sdk provides
| Part | What it is | How MindRune uses it |
|---|---|---|
SDK (sdk/) | bot.* high-level actions that check their in-game effect (walk, chop, bank, fight…), and sdk.* low-level state and actions (inventory, nearby NPCs, ground items, chat, say, dm) | Unchanged. The agent writes scripts against it. |
MCP server (mcp/) | execute_code(bot_name, code) runs a snippet with bot and sdk in scope. list_bots, disconnect_bot and get_market (Grand Exchange reads) round it out. | Unchanged. Every engine uses rs-sdk's own MCP config. MindRune wraps it from outside: list_bots also lists the owner's other avatars, and every tool result is capped at about 4,000 characters (an error's first lines always stay) and ends with a one-line game state (Engines). |
| Agent instructions | AGENTS.md / CLAUDE.md: setup, session workflow, script duration guidelines, API quick reference, pitfalls | Used with one appended ## AutoRune section. Its setup sections (creating a bot, starting a client, installing, the directory tree) and two troubleshooting entries about the client are left out of the always-loaded copy, with a pointer to the full guide, which sits next to it in the workspace. Every method name and pitfall stays. |
| Knowledge | learnings/ (banking, combat, fishing, dialogs, walking, chat, …) and wiki/ (items, NPCs, quests, shops, skills) | Mounted read-only in every avatar's workspace. Next to wiki/, MindRune mounts a read-only snapshot of LostHQ's game data in losthq/, with the wiki's slugs, and the world's interactive objects with coordinates in world/. Game data |
lab_log.md | A per-bot notes template for observations between runs | Each avatar keeps its own. MindRune's wrap-up turn appends lessons to it. |
| botclient | The enhanced web game client that relays state to the SDK and executes its actions | Run in headless Chromium while someone watches, and streamed as video. |
| lite client | A headless client (no rendering) for many bots | Used while nobody watches. |
| gateway | Connects SDK instances and game clients by username | One shared gateway. Each sandbox reaches it only through MindRune's bridge. |
| LostCity engine | The 2004scape server emulator, with rs-sdk's tweaks (faster levelling, infinite run energy, no random events) | Our own world. Every listener is bound to loopback. |
| friends server | Routes private messages | Runs as its own loopback service so PMs work. |
rs-sdk's method
rs-sdk's instructions describe a loop that MindRune keeps as the primary way of working:
- Check world state first (where am I, what do I have).
- Write a short script for one task.
- Run it, starting short (10 s for new logic, 30 s to 1 min once it works, 5+ min only for proven grinding).
- Observe and iterate: check state again, record what happened in
lab_log.md, improve the script.
"A failed 5-minute run wastes more time than five 30-second diagnostic runs." MindRune's prompts point the agent back to this loop and its guidelines instead of adding rules of their own.
Ralph loops and MindRune's autonomy loop
A "Ralph loop" keeps feeding an agent the same goal until the job is done. MindRune's autonomy loop is that idea applied to a persistent character: after every turn the runner sends the next prompt ("continue", or "choose your next goal"). The agent reports STATUS: and GOAL: lines, so an avatar never sits idle waiting for its owner.
What MindRune adds, and why
| MindRune adds | Why rs-sdk alone isn't enough for this |
|---|---|
| Owners and accounts (Discord sign-in, several avatars per owner, ownership checks everywhere) | rs-sdk is single-operator: whoever has the repo has every bot. |
| Sealed game credentials | Owners never see their avatar's game password. It is encrypted in Postgres and injected outside the sandbox. |
| Sandbox and egress allowlist | A coding agent with full tool permissions runs unattended for strangers. It must not reach the host, other bots, the database or the internet. Security |
| Provider keys and the Claude subscription engine | Each owner pays for their own AI: an encrypted API key for opencode, or their own Claude plan through the official, unmodified Claude Code CLI. Providers |
| Autonomy loop and goals | An always-on avatar needs something to send the next prompt, record goals and pick a new one when one is done. Autonomy |
TASK.md and owner direction | The owner's standing direction and the goal history must survive restarts and context compaction. MindRune keeps three sections of the agent's TASK.md up to date. |
| Skill library | rs-sdk's lab log and scripts are per bot, and its loop (write, run, observe, lab log, improve) stays primary. The library works in the background: every run (scripts and execute_code) is scored 0–1000, a script with an @skill header is shared with the owner's other avatars, and their skills and lessons appear as read-only files in family-skills/, like learnings/. Skills |
| Chat delivery and injection defence | Other players' chat reaches the agent as a marked, sanitised, untrusted block, and outgoing chat is checked for secrets and loops. Chat |
Siblings in list_bots | An avatar should know its owner's other avatars (to message them, not control them). rs-sdk has no hook for bots it doesn't connect, so a preload wraps its bot manager from outside. |
friends.md | A plain notes file per avatar for the friends it makes. No game friends list and no rs-sdk patch are needed, because PMs reach anyone online. |
| Video streaming | Watching from a phone needs the botclient's canvas encoded and fanned out to viewers. Video |
| Lifecycle and restart resume | Running avatars come back after a server restart. Stopped avatars, also those stopped by a plan limit, stay stopped until their owner presses Play. Crashes of running avatars are retried with backoff. Avatars |
World objects (world/) | The wiki has pages for NPCs, items and shops but not for the doors, stairs, ladders, railings and rocks in the world. MindRune generates them in the wiki's style from the same server content (outside rs-sdk), with options, loc ids and coordinates, read-only like wiki/. Game data |
Notes (notes/) | Avatars each re-learned the same things alone: boats leave you on a deck you must cross a gangplank from, some places trap you, and rs-sdk has helpers for loops they wrote by hand. A few short pages, read only when needed, link rs-sdk's learnings/ and sdk/API.md instead of copying them, and add only what rs-sdk has no note for. Game data |
LostHQ game data (losthq/) | The wiki has no drop rates, shop prices, exact ids or spawn coordinates. A one-time snapshot of LostHQ (taken with its permission) adds them as plain, greppable files, read-only like wiki/, with the wiki's slugs. It is data, not a game tool. Game data |
| Hiscores for MindRune avatars | A public ranking of our avatars only, built from the engine's player saves, with a wealth board for people (avatars can't read it). Hiscores |
How we wrap rs-sdk without patching it
- Workspace overlay. Each avatar's workspace is its own directory, with rs-sdk's
sdk/,mcp/,learnings/,wiki/,node_modules/and config files (and MindRune'slosthq/andworld/) bind-mounted read-only over it. The agent can read rs-sdk but can't change it. Onlybots/<its bot>/,TASK.mdand the engine's state are writable. - Gateway bridge. The SDK in the sandbox connects to what looks like the gateway on its usual port. It is really MindRune's bridge, which swaps in the real game password, pins the connection to this avatar's bot and forces controller mode.
- Preloads.
list_botssiblings come from a Bun--preloadaround rs-sdk's MCP server. The engine is made to bind to loopback by a preload on the engine process, so the upstream code that binds0.0.0.0stays untouched. - One game client per bot. rs-sdk's
execute_codewould open a browser client if none is attached. In the sandbox that is a no-op, and the agent's instructions say never to start one: rs-sdk's setup sections, which don't apply here (the bot exists, its client is MindRune's), are left out of the always-loaded guide, and the instructions say that "No game client is attached" means wait and retry. Everything else in rs-sdk's guide applies as written. The runner owns the bot's only client, so the agent and the video stream share it. - Its Puppeteer. The runner's browser client (and the dev scripts) use the Puppeteer that rs-sdk installs in
vendor/rs-sdk/node_modules, through apuppeteerpath alias intsconfig.base.json. There is one copy, so a Puppeteer bump upstream moves the runner with it.
Keeping up with rs-sdk
Updating is a submodule bump plus checks. See Operations → Updating rs-sdk. Things that matter to MindRune when rs-sdk changes:
- the MCP server still reads
bots/<name>/bot.envfrom the working directory opencode.jsonand.mcp.jsonstill name the MCP server- any new top-level path the SDK imports is added to the workspace's read-only mounts
- the item types still carry
count(the instructions teach.count, not.amount; the rs-sdk contract test checks it)
