P0 (correctness):
1. PEPA_HEADLESS=1 guard in extensions/mineflayer-bridge.ts. When `pi -p`
spawns a subprocess (banter, coach, planner, reflect, auto-patch), the
bridge no longer attempts a second MC connect — the hybrid runtime
already owns the nickname. runtime/pi-bridge.js sets the env var on
every spawn. Root cause of the "two pepa_bot's racing for the slot"
bug seen in reply-pi stderr.
2. Test state isolation in runtime/config.js. When running under the node
test runner (detected via execArgv/argv) — or when PEPA_STATE_DIR is
set — stateDir redirects to /tmp/pepa-test-state-<pid>/. log.js,
scenario-memory, world-journal, and knowledge.db all follow.
`npm test` no longer pollutes live scenarios.jsonl, world-journal.jsonl,
or daily log files. Verified empirically: post-fix run added 0 test
rows to the live scenarios file. Cleaned ~550 historical test rows
from live state in the same change.
3. defendReflex outcome reporting (runtime/reflex.js). Previously a
creeper-rule override marked the lesson succeeded=false BEFORE the
flee skill returned. Now dispatchDefendFlee accepts {lessonId} and
the onComplete fires reportAdviceOutcome with the actual flee result.
4. Mode-name → skill-id translation in runtime/coach/advice.js. Pi-coach
occasionally returns prefer_skill values that are mode names
("night_shelter", "self_preservation", "hunger"). normalisePreferSkill
maps these to SAFE_OVERRIDES entries before dispatch. Also handles
"tunnel-out", "survive_flee", "survive flee" shapes.
New behavior:
5. Self-reflection loop (runtime/coach/reflect.js). Every 30 min, the
bot asks Pi: "Are you making progress, or stuck in a loop? What
should you do differently?" Pi answers with a verdict
(progress/loop/recovering/idle/emergency), summary, next-action, and
0-N new lessons. The reflection is written to
state/<host>/reflections/<ts>.md and lessons land in the DB with
source="pi-reflect". Rate-limited to 2 calls/hour. Wired through
bot.js with the existing askPi + lastSnapshot accessor.
Tests: 246/246 green (+9 new: 6 advice mode-name + 4 reflect).
Co-authored-by: Yuriy Mayatnikov <mayatnikov@me.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(mindcraft-skills): hard timeout on every skill call
mc_avoid_enemies (and 7 other tools) wrapped only in safeCall without a
withTimeout. When mindcraft's underlying pathfinder/pvp goal couldn't be
satisfied, the call never resolved — the Pi tick loop blocked forever.
Observed live: mc_avoid_enemies pending >10 minutes after one mc_observe.
safeCall now takes timeoutMs (default 30s) and wraps withTimeout itself,
so every tool gets a hard ceiling. Per-tool overrides:
- goToPosition / goToNearestBlock: 120s / 90s (unchanged from before)
- defendSelf / avoidEnemies: 45s
- stay: secs*1000 + 10s
- craft / consume / pickup / place: 30s
- equip: 15s
collectBlock still uses its bespoke per-iter 75s loop.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(runtime): script-driven reflex daemon + Ink TUI dashboard
Pure-Pi runtime had three failure modes in practice:
- slow: 20-60s per decision because LLM was in the hot path
- expensive: every tick (defend, eat, idle) paid for a reasoning pass
- invisible: required tmux capture-pane to know what the bot was doing
New runtime/ layer is a long-running Node daemon that owns the MC
connection, ticks a priority-ordered reflex chain (defend > eat > sleep
> idle) with NO LLM in the hot path, and exposes status + commands over
a Unix-socket IPC. tui/ is an Ink dashboard that attaches over IPC and
can detach freely — multiple TUI clients can connect at once.
Pi/Codex are still available, but as on-demand escalation: TUI hotkey
'a' spawns `pi -p "<prompt>"` as a subprocess and streams stdout into
the dashboard. The self-improvement loop (proposals → operator approval
→ Pi-driven patch → hot reload) is documented in docs/runtime.md but
not yet wired.
Reflex bodies are stubs today — they log decisions but don't drive
Mineflayer actions yet. The priority chain, IPC contract, and TUI are
fully working; subsequent commits will fill in defend/eat/sleep bodies
and wire automatic escalation.
Run with `npm run bot` + `npm run tui`. Pi-only fallback stays at
`npm run agent`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Yuriy Mayatnikov <mayatnikov@me.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four targeted fixes for issues observed during the live autonomous test:
1. **bot.modes shim** (extensions/lib/mcdata.js)
Mindcraft skills.* call bot.modes.pause('cowardice') in 7+ places.
`mineflayer-modes` does not exist on npm — it's internal to Mindcraft.
attachPluginsAndInit now installs a no-op shim so skills.stay() /
.consume() / .defendSelf() etc. stop crashing with "pause undefined".
2. **Death + respawn handlers** (extensions/mineflayer-bridge.ts)
Subscribe to bot 'death' event: append diary line with position,
clear current-task.json so Pi doesn't resume a stale task referencing
inventory that no longer exists.
On 'spawn' within 5s of death: log the new respawn position to diary.
Live test had bot killed twice by zombies at night; the next Pi
prompt was unable to recover. With this it's now an explicit diary
line + clean task slate.
3. **Auto-defend reflex tick** (extensions/mineflayer-bridge.ts)
New setInterval(2s) that, when bot.health < 18 AND a hostile mob
(zombie/skeleton/creeper/spider/etc.) is within 6 blocks AND no
active world task, fires `bot.pvp.attack(nearest)` in the background.
No LLM call needed for instant self-defense — saves tokens and reacts
on mineflayer timescale (sub-second) rather than Pi loop timescale
(~10s+ to reason and dispatch). Throttled to once per 4s.
4. **mc_collect_block bulk rework** (extensions/mindcraft-skills.ts)
Live test showed count=1 succeeds in ~25s but count=8 hangs past
270s with identical blocks in range. Upstream collectBlock plugin
appears to drift on its block cache after the first dig in dense
terrain. Loop single-block collects in-tool instead (75s per iter,
re-pathfind on each iteration). Track per-iter success/failure,
abort after 3 consecutive failures, return aggregate count to the
agent so it can adapt instead of seeing a single failure.
Smoke test: both extensions load, bot connects, perception confirms
hostiles nearby and daylight safety check. No syntax/runtime errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live test showed count=1 succeeds in ~25s but count=8 timed out at 90s
because gathering 8 logs naturally takes ~200s of pathing + dig + pickup
across the area. Fixed 90s ceiling was too tight for legitimate bulk
collection.
Scale timeout linearly: 30s overhead + 30s per requested block, capped
at 600s. count=1 → 60s, count=4 → 150s, count=8 → 270s, count=20 → 630s
(capped at 600). Catches real hangs (wrong name, unreachable) without
killing legitimate long collections in dense forest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mineflayer's collectBlock plugin and pathfinder can hang indefinitely when:
- the requested block name doesn't match anything in range (e.g. asking for
"oak_log" when the only nearby logs are "dark_oak_log"),
- pathfinder cannot reach the goal but doesn't return a clean noPath,
- a path computation enters an infinite-search state in dense terrain.
This blocks the entire Pi tool loop — observed in a live test where
mc_collect_block("oak_log", 4) ran for 8+ minutes without ever returning,
leaving Pi unable to respond to chat or do anything else.
Add a 90s timeout for collectBlock/goToNearestBlock and 120s for goToPosition.
On timeout the tool throws a descriptive error so the agent learns to retry
with a different block name or position rather than waiting forever.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bot was acting blind: it knew its own coordinates but nothing about
what was around it. mc_goto's over-strict safety guards refused every
real path. mc_dig was too low-level to drive a coherent farming loop.
The result: 4+ hours of "trying" with zero physical achievement.
This commit reframes the bridge around a perceive→decide→act loop using
proven primitives from Mindcraft (github.com/kolbytn/mindcraft, MIT —
LICENSE-MINDCRAFT vendored beside the library files).
Changes:
- extensions/lib/ (new, vendored from Mindcraft with attribution)
- world.js (431 LoC) — 21 perception functions: getNearbyBlockTypes,
getNearbyEntities, getInventoryCounts, getNearestBlock, getPosition,
getBiomeName, etc.
- skills.js (2093 LoC) — 30+ action primitives: collectBlock, placeBlock,
goToPosition, goToNearestBlock, craftRecipe, equip, consume,
defendSelf, avoidEnemies, pickupNearbyItems, stay, etc.
- mcdata.js (~600 LoC) — Mindcraft's mc-data adapter. Imports patched
to local paths; mineflayer-auto-eat removed (our installed 5.x has
a divergent API; skills.consume() falls back to bot.consume()).
Added attachPluginsAndInit(bot) export so mineflayer-bridge.ts can
wire plugins onto its externally-created bot.
- settings.js — minimal stub with farmer-bot defaults.
- extensions/mindcraft-skills.ts (new, 406 LoC) — Pi extension registering
15 high-level tools on top of the vendored library:
- Perception: mc_observe, mc_inventory, mc_nearby_blocks, mc_nearby_entities
- Action: mc_collect_block, mc_place_block, mc_go_to, mc_go_to_block,
mc_craft, mc_equip, mc_consume, mc_defend_self, mc_avoid_enemies,
mc_stay, mc_pickup_nearby
ES modules from extensions/lib/ are loaded via dynamic import() at
extension init so the cross-extension require()-race resolves cleanly.
- extensions/mineflayer-bridge.ts
- Expose the live Mineflayer bot on globalThis.__pepaPiBot so the
mindcraft-skills extension can use it (set on connect, cleared on
error/end/manual disconnect).
- Call attachPluginsAndInit(nextBot) right after createBot to load
pathfinder, pvp, collectblock, armorManager and prime
minecraft-data once login completes.
- package.json — new runtime deps: minecraft-data, vec3,
mineflayer-pvp, prismarine-item.
- AGENTS.md — new "Perception → decision → action" section before
"Your tools right now" with full tool catalog and a deprecation
note for the broken mc_goto / mc_build_pyramid_5x5 / low-level
mc_dig from the old bridge.
Smoke test (medium thinking): bot called mc_observe and received a
real JSON snapshot — nearbyBlocks listed coal_ore, oak_log, water,
sand; nearbyEntityTypes listed creeper, zombie, pillager, skeleton.
The bot can finally see what it could not see this morning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial seed for an autonomous, self-extending Minecraft player powered by
Pi (pi.dev) and Mineflayer.
Includes README, AGENTS.md mandate, .env.example, MIT LICENSE, package.json
with mineflayer + dotenv, and empty skills/ extensions/ prompts/ dirs for
the agent to grow into.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>