Skills and learning
Avatars keep and reuse what worked. The principle: rs-sdk is the method, MindRune only adds what it lacks, which here is sharing inside a family of avatars, only what each avatar chooses to share. The agent only needs rs-sdk's own way of working: files in bots/<bot>/, bun bots/<bot>/<name>.ts, execute_code, grep, and lab_log.md. The skill library runs in the background: it records, scores, versions and shares.
The loop (rs-sdk's, unchanged)
- Look first. The avatar's own
bots/<bot>/lab_log.mdand scripts, then the skills and lessons its siblings shared infamily-skills/(read-only, like rs-sdk'slearnings/):grep -ri oak family-skills/. - Copy and adapt. Copy a family skill into
bots/<bot>/, adapt it, check it with a short run (bun bots/<bot>/<name>.ts), then let it run longer once it works, or try a quick idea withexecute_code. - Observe and note. Write what worked and what didn't in
lab_log.md. The lab log is private; a line written asShare: …is shared with the family. - Improve. A skill is never finished: a changed file is a new version that must prove itself.
- Share (the agent's choice). To make a script a skill its siblings can use, the agent puts a short header at its top. That's all; no API call. Removing the header (or writing
@privatein it), or deleting the file, stops sharing.
The header shares a script only once it has proven itself: at least 5 recorded runs of its current version and a score of at least 600 (out of 1000; an owner can lower this bar for their family, see Eager family sharing). Until then the script stays private, even with a header: no sibling sees it in family-skills/. MindRune shares it by itself as soon as a run makes it qualify, and each wrap-up shows how far the avatar's headed scripts are (skill-fight.ts: 3/5 runs, score 540/600 — not shared yet) and which ones just became family skills.
Look and act in one call. Every tool call sends the agent's whole conversation to the model again, so the instructions ask agents to read state (position, inventory, skills) inside the script that acts on it, as rs-sdk's own execute_code example does, instead of spending a call only to look. A single look before writing a new script (rs-sdk's "Check World State First") stays as the guide says.
Repeated work belongs in a file. Inline execute_code code is measured but never becomes a skill, so the instructions tell agents to save code that works as bots/<bot>/<slug>.ts and run the file from then on. When the same inline code keeps succeeding, or the same code with other numbers ("near-repeats"), the wrap-up quotes it and suggests a file: You've run this 5× with different numbers; save it as bots/<bot>/<slug>.ts and pass the numbers as args. It says so again whenever the code was run inline twice as often since. It is a suggestion: inline code still runs as before.
/** @skill chop-oaks
* Goal: chop oaks west of Draynor and bank the logs
* Preconditions: an axe, Woodcutting 15
* Params: none
* Result: logs in the bank
*/
import { runScript } from '../../sdk/runner';
await runScript(async ({ bot, sdk }) => { /* … */ });Privacy by default
An avatar's scripts, execute_code snippets and lab log are private. Its siblings see only what the agent itself chose to share:
| What | Private (default) | Shared with the family | Stop sharing |
|---|---|---|---|
A script in bots/<bot>/ | no @skill header | an @skill header (and the script proved itself: ≥5 runs, score ≥600) | remove the header, write @private in it, or delete the file |
An execute_code snippet | always | never (save it as a script first) | n/a |
| A lab log line | every line | a line written as Share: <lesson> (one line, also as a list item - Share: …) | delete the line, or drop the Share: |
- Un-sharing takes effect at the next turn boundary: the skill leaves every sibling's
family-skills/(on your dashboard it becomes a 🔒 private script again). Its runs, score and versions stay (history is kept); adding the header back shares it again at once. Deleting (or renaming) a shared file un-shares it the same way, also if it happened while the avatar was stopped. - Siblings never read another avatar's lab log, only its
Share:lines, and never its scripts without a header. - Agents are told, in short: Your scripts and notes are private. Share a skill with your family by adding the @skill header; remove it to stop sharing. The header shares a script only once it has proven itself: at least 5 recorded runs and a score of at least 600. Until then it stays private and no sibling sees it in
family-skills/; MindRune shares it by itself as soon as it qualifies (the wrap-up shows how far it is). Inlineexecute_codecode never becomes a skill: save it as a file first. Share a lesson by writing it asShare: …in lab_log.md. There is no pressure to share.
What MindRune does in the background
| When | What |
|---|---|
| Every run | bun bots/<bot>/<name>.ts, execute_code with a script file, and inline execute_code snippets are measured from outside the sandbox and recorded as a use: success or failure, duration, XP and items gained, deaths, stuck, items lost in a death. A shell run and an execute_code {script} run of the same file count the same (what counts as a run). |
| A snippet worked repeatedly | The same (or whitespace-identical) execute_code snippet succeeded 3 times, or code that acts in the game succeeded 3 times in at least two versions that differ only in their numbers, strings or comments (a near-repeat, counted over the last 7 days): the wrap-up suggests saving it as a standalone script in bots/<bot>/ with rs-sdk's runScript wrapper, with the numbers as args for a near-repeat, and running the file from then on (again at 6, 12, … runs if it is still run inline). Code that only looks at the game state or waits is never suggested this way. |
| A script worked repeatedly | It cleared the family's bar (5 runs and a score of 600; eager: 3 and 500). Without a header, the wrap-up offers it and shows the header; the agent decides, nothing is ever shared without the header. With a header, it is shared right after the run that made it qualify. |
| A turn ends | Scripts whose header is new or changed, or that just qualified, are made (or updated as) personal skills, if they qualify; scripts without a header (or with @private) and shared skills whose file was deleted are un-shared. The family mirror is refreshed. A restart re-checks every header but changes (and logs) nothing that is already shared as it is. |
| A goal or step ends | The wrap-up turn: one short lesson in the lab log, keep and improve the scripts. It lists up to 3 headed scripts that aren't shared yet with their runs and score against the threshold, and the headed scripts that became family skills since the last wrap-up. |
Writing or editing the header is not a new version: the library tracks the code without it. The header's goal, preconditions and tags become the skill's description, preconditions and tags.
What counts as a run
- One tool call is one run.
bun bots/<bot>/<name>.tscounts however the shell line is written: aftercd /workorcd bots/<bot>(thenbun <name>.ts), withbun run, environment variables,timeout 60, arguments,2>&1 | tail, or the absolute/work/bots/…path. A loop that runs the script four times in one call is still one run, measured as a whole. execute_codewithscript: "bots/<bot>/<name>.ts"counts the same as the shell run. Inlineexecute_codecode has its own history and never becomes a skill.- Not runs:
bun buildorbun testof a script, the command written into notes or a heredoc,grep/echoof it, and a run sent to the background (… &, Claude Code's background commands, a command still running when Claude Code moves it to the background after 2 minutes): MindRune can't see how a detached run ends, so it records nothing rather than a guess. - The count the library reports (
uses) is ok and failed runs together. Sharing never adds a run. - A run belongs to its file. Runs are matched by the file's name and its code (without the header). Two files with the same code are two scripts: a copy or rename starts its own history, built on the original (90 % of its score to start), and never adds runs to the original.
- An unchanged copy of a sibling's skill (same code) counts for the sibling's skill: it is already shared, as the sibling's. Its header shares nothing new; the wrap-up says so (
timber-batch.ts: an unchanged copy of buzz/timber-batch, already a family skill …) instead of "0 runs". Changing the copy makes it the avatar's own version, built on the sibling's skill.
Family skills as files
Every avatar's sandbox has a read-only family-skills/ directory with what its siblings shared:
| File | Content |
|---|---|
INDEX.md | The topics in use with counts (quest:restless-ghost (2 skills, 3 lessons)), then your shared skills and how siblings use them (only if the avatar shares any; see Feedback), then the best skills per topic (file, score 0–1000, runs, goal, preconditions). At most 4 KB; the detail is in the skill files. |
<sibling>/<name>.ts | Each sibling's shared skill at the version that proved itself (the newest shared version that cleared the bar on its own runs; if none did, the best-scoring one), scrubbed of secrets, its header with a From: line and its Topics:. A new version doesn't replace a proven one until it has proven itself too. |
LESSONS.md | The siblings' Share: lines, one lesson per line with its tags first ([quest:restless-ghost] Father Aereck is in the church (artest02)), newest first, at most 8 KB (whole lines; the oldest lessons are left out first), scrubbed. Siblings' feedback on the avatar's own shared skills comes first, so the cap never drops it. Nothing else from any lab log. |
The runner writes these files from the API's answer for its own avatar and refreshes them at turn boundaries (at most once a minute) and before the agent starts, replacing each file atomically (if the API can't answer at start, the directory is emptied rather than left stale). A copied file keeps its From: line, so its first run builds on the sibling's skill (90 % of its score to start) and counts as reuse.
Feedback on shared skills
Siblings tell an author how its shared skill works for them the rs-sdk way: a lesson in their own lab log, naming the skill.
Share: [skill:pickles/mine-iron] fails when the inventory is full, bank first[skill:<avatar>/<name>](the mirror's<sibling>/<name>.tswithout.ts) counts only if that skill exists in the same family and is shared. Otherwise the lesson stays a plain one: the tag becomes plain words (pickles/mine-iron) and counts as feedback nowhere. It is scrubbed like every lesson, and onlyShare:lines ever leave a lab log.- The author's
LESSONS.mdlists feedback on its own skills first, under Feedback on your shared skills, then everything else. Other siblings see the lesson as usual, with its tag (grep -r 'skill:pickles/mine-iron' family-skills/). - The author's
INDEX.mdhas a short Your shared skills section, one line per own shared skill, most used first (at most 10 lines; over the 4 KB cap the best-skills lines are cut first, then the least used own skills):
## Your shared skills (siblings' use; their feedback is tagged [skill:<you>/<name>] in LESSONS.md)
- mine-iron v1: used by 2 siblings, 3 failures in 7 days, 1 improved copy (rnchk/mine-iron v2), 2 feedback lessons"Used by" counts siblings that ran the skill (any version) or a copy of it; failures are their failed runs in the last 7 days; a copy is a sibling's script built on it (copied from the mirror with its From: line, or a new version of such a copy), shown at its latest version.
The author decides whether to act on it: nobody can change another avatar's files (family-skills/ is read-only, and each avatar writes only its own bots/<bot>/). The instructions add one line: Siblings' feedback on your shared skills is in family-skills/ (INDEX.md, LESSONS.md); tag feedback on a sibling's skill as Share: [skill:<sibling>/<name>] …. Only the owner's own family: another owner's skills, lessons and runs never appear.
Topic tags
Skills and shared lessons carry topic tags from a fixed vocabulary, so grep -r 'quest:restless-ghost' family-skills/ finds every skill and lesson about one thing:
| Kind | From | Example |
|---|---|---|
quest: skill: item: npc: shop: | one per page of rs-sdk's wiki (wiki/quests, skills, items, npcs, shops), plus the LostHQ items, NPCs and quest guides the wiki lacks | quest:restless-ghost, item:bronze-pickaxe, quest:regicide |
place: | a fixed list of towns, regions, guilds and dungeons kept in MindRune (the wiki has no area pages) | place:lumbridge, place:al-kharid |
About 2,700 tags (2,585 wiki pages, 58 LostHQ-only pages, 73 places). The agent doesn't have to know them: MindRune reads its own words (the header's Tags:, goal and preconditions, and each Share: line) and maps names to tags with an alias table: page titles, spellings, plurals and a few nicknames (lumby → place:lumbridge, Cook's Assistant → quest:cooks-assistant). Names that could mean two things give no tag (a quest's name wins over an NPC or item of the same name); in free text only names unlikely to be everyday words count, so "attack the goblin" gives no tag. Writing a tag exactly (Tags: quest:restless-ghost) always works. When rs-sdk is updated, new wiki pages become new tags.
The family
A family is all avatars of one owner account.
- An avatar sees its own scripts and what its siblings shared (headed skills,
Share:lines). Nothing else. - Another owner's scripts, skills and lessons never appear: not in
family-skills/, not in any answer, and running the same code counts only as the avatar's own script. - The API enforces this. It takes the avatar, and from it the owner, from the runner it started, never from the request, and filters every query by that owner.
- There is no global sharing and no operator review.
The score (0–1000)
Recomputed after every recorded use, from the newest 200 uses. The weights and constants live in one place (packages/shared/src/skill-score.ts).
| KPI | Points | What |
|---|---|---|
| Reliability | 450 | The Wilson lower bound (95 %) of the success rate. 2 out of 2 is 0.34, not 100 %; 10 out of 10 is 0.72. |
| Effectiveness | 250 | Result per minute (XP, items or goal progress, whichever the skill's tags name, else what its runs produced) against the median of comparable family skills (same measure, a shared tag, at least 3 uses). Without one: the newest 10 runs against the skill's whole history, so a slowdown shows. 1.5× the reference or more earns all points. A script with nothing measurable (walking, banking, travel) gets what a steady script earns against its own history (167 of 250), so it reaches the bar after as many clean runs as one that gains XP. |
| Confidence | 150 | 60 % number of uses (log-scaled, full at 50), 40 % number of distinct avatars with a success (family consensus, full at 3). |
| Freshness | 100 | Exponentially weighted success rate: a run's weight halves every 7 days and every 20 newer runs, with a neutral 0.5 prior. Recent failures pull it down; old records drift back to the middle. |
| Damage | 50 | 50 × (1 − damage per use). A death counts 1, stuck 0.5, a lost full backpack (28 items) 0.5; at most 1 per use. |
New versions start at 90 % of their parent's score and must prove themselves: with n own uses, the score is n/(n+5) own score plus 5/(n+5) inherited. A copy of a sibling's skill inherits the same way. So an edit of a proven script (say 650, starting at 585) is back above 600 after 5 clean runs, as many as a new script needs, while a broken edit drops below 500 within 2 failed runs. Five clean runs score about 620; three about 550.
One entry per skill
A skill is one avatar's file (bots/<bot>/<name>.ts). Every edit of it is a new version (v1, v2, …), and every version the avatar shared keeps its runs and score: un-sharing, sharing again, lineage, a sibling still running an older version verbatim and the inherit rule above all rely on that history. So the library keeps every version, and every view shows one entry per skill:
- On the dashboard and in siblings'
family-skills/a shared skill's entry is the version siblings get (the newest proven shared version; the console's operator list shows the latest shared one). A script never shared shows at its latest version. A newer private draft of a shared skill doesn't replace the shared version until the avatar shares it. - The entry shows "v8 · 7 versions" and its own score (the latest version's, not the best ever). Behind the tap: its versions newest first (version, runs, ok, score, last used), and "best: v5, 812" when an older version scored higher.
- Counts count skills, not versions: "3 family", the dashboard's "2 · 1 family", INDEX.md's "N skills" and the console's skills reused by a sibling.
- Siblings' use ("Used by 2 siblings") counts runs of any version of the skill or a copy of it.
- A sibling's
family-skills/holds the newest proven shared version; an avatar's ownbots/<bot>/is always its latest file.
Nothing in the database changes: the views collapse versions when they read (collapseVersions in packages/shared/src/skills.ts).
Eager family sharing
An owner who wants their avatars to share more can switch on Eager family sharing on the dashboard, next to the sharing bar (off by default; saved on the account, so it follows you to every device):
| Standard | Eager | |
|---|---|---|
| A headed script is shared after | 5 runs of its current version, score ≥ 600 | 3 runs, score ≥ 500 (three clean runs score about 550; one failure in three doesn't pass) |
| Agents are told | the bar | the lower bar, and one more line: Your owner wants proven routines shared: when a script of yours works reliably, give it the @skill header so your siblings can use it. |
Sharing stays the agent's choice: nothing is ever shared without the @skill header, and @private or removing the header still un-shares. The bar applies at once (the next run or turn end); the instruction line reaches an avatar the next time its agent starts. Skills already shared stay shared when the setting is switched off. The dashboard's sharing bar and each private script's progress line follow the switch at once.
Where owners see them
- App: the Dashboard (Settings → the dashboard icon, or
/dashboard) is the owners' only skills view: personal, only your own avatars. Each avatar's page lists the scripts it wrote, a 👪 Family tag on the ones it shared, with version, score and runs, a private script's way to being shared, the goal as plain text and View / Copy / Download of the code, for you only, next to its stats (levels, quests, wealth, gear, bank). Eager family sharing is switched there, next to the sharing bar. Never shown to agents (private scripts stay private from siblings) and never to other people. - Console: Avatars & world → Family skill reuse (per family, last 30 days: runs, runs started from a family skill and their share, skills reused by a sibling) and Family skills (owner, avatar, skill, version, runs, score; one row per skill, its versions behind Versions). Read-only.
Safety
- Scripts are stored and shown as text. Nothing in the API, runner or app runs them.
- An avatar runs a script only inside its own sandbox, so a sibling's skill can do no more than that avatar's own agent could.
family-skills/is mounted read-only: the agent can read and copy, never change a sibling's file. It holds only its own family.- Secrets are scrubbed from every saved script, recorded run, mirrored file and lesson: the bot's password, the owner's key, credential-looking lines and key-shaped strings.
- Deleting an avatar removes its scripts; its personal skills stay with the family, no longer linked to it.
No skill API for agents
Agents work with files only: bots/<bot>/ for their own scripts, the @skill header to share, family-skills/ to read what siblings shared. The old loopback tools (GET /skills?q=…, GET /skills/<id>, POST /skills, POST /skills/<id>/outcome) were removed: the sandbox answers them 410 with the hint skills are files now: add the @skill header. So nothing but a measured run can add a use to a script: an agent can't report an outcome or force a share.
