feat(runtime): observability + no-progress detector (Phase 1)
Phase 1 of plans/autonomous-survival-bot-prd.md. The bot must always be able to answer "what am I doing and why am I not doing more?" without parsing the log stream. New modules: - runtime/state.js: pure FSM classifier emitting emergency / working / recovering / planning / social / idle from snapshot + reflex context. - runtime/no-progress.js: sliding-window detector that watches position and inventory; when both are unchanged for 60 s+, emits one stable reason code from REASONS (waiting_for_day, night_hostile_nearby, no_food_source, inventory_full, no_reachable_target, planner_empty, awaiting_action_cooldown). - runtime/viewer.js: optional prismarine-viewer launcher behind VIEWER_PORT. Lazy import so the dep is not required by default. Wiring: - runtime/bot.js: tick() now computes runtimeState + noProgressReason every tick and stamps them on the snapshot along with activeSkill, currentMilestone (read from plan.md, cached 30 s), lastResult, failuresByCode and lastEscalation. - runtime/bot.js: dispatchAction records lastResult and lastFailureAt for the recovering-state classifier. - runtime/planner.js: exports isPlannerBusy(), readNextMilestone() and planExists() so the runtime can show planning state + current milestone without spawning extra Pi calls. - runtime/config.js: adds VIEWER_PORT support. TUI: - tui/tui.tsx: StatusBar gains a state badge, current-skill row, milestone row, no-progress reason warning, last-result line with ok/fail color, failures-by-class summary and last-escalation age. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
// Optional prismarine-viewer launch — local visual debugging surface.
|
||||
// Activated by setting VIEWER_PORT in .env (e.g. 3007). The dependency is
|
||||
// not required at runtime: if `prismarine-viewer` is not installed, this
|
||||
// module logs once and returns, so production deploys aren't forced to
|
||||
// carry the extra dep.
|
||||
|
||||
import { info, warn } from "./log.js";
|
||||
import { config } from "./config.js";
|
||||
|
||||
let started = false;
|
||||
|
||||
export async function maybeStartViewer(bot) {
|
||||
if (started) return;
|
||||
const port = config.viewerPort;
|
||||
if (!port) return;
|
||||
let mineflayerViewer;
|
||||
try {
|
||||
({ mineflayer: mineflayerViewer } = await import("prismarine-viewer"));
|
||||
} catch (e) {
|
||||
warn("viewer", `VIEWER_PORT=${port} requested but prismarine-viewer is not installed (npm i prismarine-viewer)`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mineflayerViewer(bot, { port, firstPerson: false });
|
||||
started = true;
|
||||
info("viewer", `prismarine-viewer listening on http://localhost:${port}`);
|
||||
} catch (e) {
|
||||
warn("viewer", `failed to start: ${e?.message ?? e}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user