feat(runtime/coach,persona): death post-mortem coach + Russian chat narration
Coach (runtime/coach/postmortem.js):
- bot.on('death') → captures context (last skill, hostile, recent
scenarios, journal nearby, snapshot) → inserts row into knowledge.deaths
- Periodic drain (every 5min, ≤3 Pi calls/hour, 12min cooldown):
batches up to 8 unanalysed deaths, asks Pi to extract 1-3 generalised
lessons in JSON, persists to knowledge.lessons + knowledge.postmortems
- Pi prompt asks for structured advice (trigger_skill, trigger_hostile,
avoid_skill, prefer_skill, confidence) so future dispatch can act on it
Persona (runtime/persona/chatter.js):
- Polls snapshot every 5s, narrates Russian lines on transitions:
skill start, threat spotted, dusk/dawn, respawn, stuck, milestone done
- Rate-limited: min 75s gap, max 8/hour, duplicate suppression
- ~14 template buckets covering gather/craft/build/travel/combat/weather
Wire-up in bot.js (5 lines):
- initKnowledge({stateDir}) fire-and-forget at module load
- attachCoach + attachChatter inside bot.once("spawn")
Tests: 14 new (7 coach, 7 persona). Total suite 230 green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -57,10 +57,18 @@ import { createSkillMetrics } from "./skill-metrics.js";
|
||||
import { createWorldJournal } from "./world-journal.js";
|
||||
import { createScenarioMemory, situationHash } from "./scenario-memory.js";
|
||||
import { createOwnedBlocksLedger } from "./owned-blocks.js";
|
||||
import { initKnowledge } from "./knowledge/index.js";
|
||||
import { attach as attachCoach } from "./coach/postmortem.js";
|
||||
import { attach as attachChatter } from "./persona/chatter.js";
|
||||
|
||||
fs.mkdirSync(stateDir, { recursive: true });
|
||||
const JOINED_FLAG = path.join(stateDir, "joined-before.flag");
|
||||
|
||||
// Knowledge subsystem boots in the background. If better-sqlite3 isn't
|
||||
// installed the call returns false and every knowledge API becomes a
|
||||
// safe no-op. See docs/v0.2.0-self-learning.md.
|
||||
initKnowledge({ stateDir }).catch((e) => warn("knowledge", `init failed: ${e?.message ?? e}`));
|
||||
|
||||
// Auto-escalation tunables. With tick=3s, 20 noops ≈ 1 minute idle before we
|
||||
// even consider asking Pi. Cooldown prevents spamming the LLM when the bot
|
||||
// is permanently stuck on the same situation.
|
||||
@@ -658,6 +666,11 @@ function connect() {
|
||||
pathWatchdog = createPathfinderWatchdog(bot);
|
||||
info("pathfinder", "stuck-replan watchdog armed");
|
||||
} catch (e) { warn("pathfinder", `watchdog start failed: ${e?.message ?? e}`); }
|
||||
// v0.2.0 — self-learning coach + persona narration. Both are
|
||||
// import-safe; they just attach listeners and (for coach) a periodic
|
||||
// Pi-drain timer. See docs/v0.2.0-self-learning.md.
|
||||
try { attachCoach(bot, { stateDir, askPi }); } catch (e) { warn("coach", `attach: ${e?.message ?? e}`); }
|
||||
try { attachChatter(bot, { getSnapshot: () => lastSnapshot }); } catch (e) { warn("persona", `attach: ${e?.message ?? e}`); }
|
||||
});
|
||||
|
||||
bot.on("messagestr", (text) => {
|
||||
|
||||
Reference in New Issue
Block a user