0.6.0 training session: Oracle Bot, RL combat, Mind's Eye, multilingual pipeline

Major changes from this session:

Training:
- 0.6.0 training running: 9B on steel141 3090 Ti, 27B on rented H100 NVL
- 7,256 merged training examples (up from 3,183)
- New training data: failure modes (85), midloop messaging (27),
  prompt injection defense (29), personality (32), gold from quarantine
  bank (232), new tool examples (30), claude's own experience (10)
- All training data RCON-validated at 100% pass rate
- Bake-off: gemma3:27b 66%, qwen3.5:27b 61%, translategemma:27b 56%

Oracle Bot (Mind's Eye):
- Invisible spectator bot (mineflayer) streams world state via WebSocket
- HTML5 Canvas frontend at mind.mortdec.ai
- Real-time tool trace visualization with expandable entries
- Streaming model tokens during inference
- Gateway integration: fire-and-forget POST /trace on every tool call

Reinforcement Learning:
- Gymnasium environment wrapping mineflayer bot (minecraft_env.py)
- PPO training via Stable Baselines3 (10K param policy network)
- Behavioral cloning pretraining (97.5% accuracy on expert policy)
- Infinite training loop with auto-restart and checkpoint resume
- Bot learns combat, survival, navigation from raw experience

Bot Army:
- 8-soldier marching formation with autonomous combat
- Combat bots using mineflayer-pvp, pathfinder, armor-manager
- Multilingual prayer bots via translategemma:27b (18 languages)
- Frame-based AI architecture: LLM planner + reactive micro-scripts

Infrastructure:
- Fixed mattpc.sethpc.xyz billing gateway (API key + player list parser)
- Billing gateway now tracks all LAN traffic (LAN auto-auth)
- Gateway fallback for empty god-mode responses
- Updated mortdec.ai landing page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Seth
2026-03-22 20:22:50 -04:00
parent baab24f8b1
commit 5b28002001
44 changed files with 20873 additions and 4352 deletions
+232
View File
@@ -0,0 +1,232 @@
/**
* explorer_bot.js — Claude explores the world through a mineflayer bot's eyes.
*
* Connects to the server, reports what it sees, and accepts movement commands
* via stdin. Outputs structured JSON observations.
*
* Usage: node explorer_bot.js [host] [port]
*/
const mineflayer = require('mineflayer');
const { Vec3 } = require('vec3');
const readline = require('readline');
const host = process.argv[2] || '192.168.0.244';
const port = parseInt(process.argv[3] || '25567', 10);
const bot = mineflayer.createBot({
host,
port,
username: 'ClaudeBot',
auth: 'offline',
version: '1.21.11',
});
// ── Observation functions ──
function observe() {
if (!bot.entity) return '{"error": "not spawned"}';
const pos = bot.entity.position;
const obs = {
position: { x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) },
health: bot.health,
food: bot.food,
gamemode: bot.game.gameMode,
time: bot.time.timeOfDay,
isRaining: bot.isRaining,
};
// Block below and around
obs.blockBelow = blockName(pos.offset(0, -1, 0));
obs.blockAt = blockName(pos);
obs.blockAbove = blockName(pos.offset(0, 2, 0));
// Look in all 4 directions for interesting blocks
obs.surroundings = {};
const dirs = { north: [0, 0, -1], south: [0, 0, 1], east: [1, 0, 0], west: [-1, 0, 0] };
for (const [name, [dx, dy, dz]] of Object.entries(dirs)) {
const blocks = [];
for (let dist = 1; dist <= 8; dist++) {
const b = blockName(pos.offset(dx * dist, 0, dz * dist));
if (b !== 'air') blocks.push({ dist, block: b });
}
if (blocks.length) obs.surroundings[name] = blocks;
}
// Nearby entities
obs.entities = [];
for (const entity of Object.values(bot.entities)) {
if (entity === bot.entity) continue;
const dist = entity.position.distanceTo(pos);
if (dist <= 20) {
obs.entities.push({
type: entity.type === 'player' ? 'player' : (entity.name || entity.type),
name: entity.username || null,
distance: Math.round(dist),
position: {
x: Math.floor(entity.position.x),
y: Math.floor(entity.position.y),
z: Math.floor(entity.position.z),
},
});
}
}
// Scan a 5x5 area at foot level for terrain overview
obs.terrain = [];
for (let dx = -5; dx <= 5; dx++) {
for (let dz = -5; dz <= 5; dz++) {
// Find surface block (scan down from y+3)
for (let dy = 3; dy >= -3; dy--) {
const b = bot.blockAt(pos.offset(dx, dy, dz));
if (b && b.name !== 'air') {
obs.terrain.push({ dx, dz, y: Math.floor(pos.y) + dy, block: b.name });
break;
}
}
}
}
return JSON.stringify(obs);
}
function blockName(vec) {
const b = bot.blockAt(vec);
return b ? b.name : 'unloaded';
}
// ── Movement functions ──
async function walkTo(x, y, z) {
const goal = new Vec3(x, y, z);
console.log(JSON.stringify({ action: 'walking', target: { x, y, z } }));
// Simple movement: look at target and walk forward
await bot.lookAt(goal);
bot.setControlState('forward', true);
// Walk until close or timeout
return new Promise((resolve) => {
const check = setInterval(() => {
const dist = bot.entity.position.distanceTo(goal);
if (dist < 2) {
bot.setControlState('forward', false);
clearInterval(check);
resolve(true);
}
}, 200);
setTimeout(() => {
bot.setControlState('forward', false);
clearInterval(check);
resolve(false);
}, 10000); // 10s max walk
});
}
async function lookAround() {
const snapshots = [];
const yaws = [0, Math.PI / 2, Math.PI, -Math.PI / 2]; // N, E, S, W
const names = ['north', 'east', 'south', 'west'];
for (let i = 0; i < 4; i++) {
await bot.look(yaws[i], 0, true);
await new Promise(r => setTimeout(r, 500));
// What do I see in this direction?
const pos = bot.entity.position;
const dx = Math.round(Math.sin(yaws[i]));
const dz = Math.round(-Math.cos(yaws[i]));
const visible = [];
for (let dist = 1; dist <= 16; dist++) {
const b = bot.blockAt(pos.offset(dx * dist, 0, dz * dist));
const bUp = bot.blockAt(pos.offset(dx * dist, 1, dz * dist));
const bDown = bot.blockAt(pos.offset(dx * dist, -1, dz * dist));
if (b && b.name !== 'air') visible.push({ dist, block: b.name, level: 'eye' });
if (bDown && bDown.name !== 'air' && bDown.name !== (b && b.name)) visible.push({ dist, block: bDown.name, level: 'ground' });
}
snapshots.push({ direction: names[i], blocks: visible.slice(0, 8) });
}
return snapshots;
}
// ── Event handlers ──
bot.on('login', () => {
console.log(JSON.stringify({ event: 'login', username: bot.username }));
});
bot.on('spawn', async () => {
console.log(JSON.stringify({ event: 'spawn', ...JSON.parse(observe()) }));
// Initial look around
const views = await lookAround();
console.log(JSON.stringify({ event: 'look_around', views }));
});
bot.on('chat', (username, message) => {
if (username === bot.username) return;
console.log(JSON.stringify({ event: 'chat', from: username, message }));
});
bot.on('health', () => {
console.log(JSON.stringify({ event: 'health', health: bot.health, food: bot.food }));
});
bot.on('death', () => {
console.log(JSON.stringify({ event: 'death' }));
});
bot.on('kicked', (reason) => {
console.log(JSON.stringify({ event: 'kicked', reason: typeof reason === 'object' ? JSON.stringify(reason) : reason }));
});
bot.on('error', (err) => {
console.error(JSON.stringify({ event: 'error', message: err.message }));
});
// ── Command interface (stdin) ──
const rl = readline.createInterface({ input: process.stdin });
rl.on('line', async (line) => {
const cmd = line.trim();
if (cmd === 'observe' || cmd === 'look') {
console.log(observe());
}
else if (cmd === 'around') {
const views = await lookAround();
console.log(JSON.stringify({ action: 'look_around', views }));
}
else if (cmd.startsWith('walk ')) {
const parts = cmd.split(' ');
const x = parseInt(parts[1]), y = parseInt(parts[2]), z = parseInt(parts[3]);
const arrived = await walkTo(x, y, z);
console.log(JSON.stringify({ action: 'walk_result', arrived, ...JSON.parse(observe()) }));
}
else if (cmd === 'forward') {
bot.setControlState('forward', true);
setTimeout(() => {
bot.setControlState('forward', false);
console.log(observe());
}, 3000);
}
else if (cmd === 'jump') {
bot.setControlState('jump', true);
setTimeout(() => bot.setControlState('jump', false), 500);
setTimeout(() => console.log(observe()), 1000);
}
else if (cmd.startsWith('say ')) {
bot.chat(cmd.slice(4));
}
else if (cmd === 'quit') {
bot.end();
process.exit(0);
}
else {
console.log(JSON.stringify({ error: 'unknown command', commands: ['observe', 'around', 'walk X Y Z', 'forward', 'jump', 'say MSG', 'quit'] }));
}
});