Five concrete patterns from Voyager and Mindcraft, applied in our shape
without abandoning the git-as-evolution-substrate that makes pepa
distinct. Plus a first multi-agent surface so two bots from the same
repo can share intent.
1. runtime/critic.js (Voyager critic.txt)
- Spawns `pi -p` with a JSON-only critic prompt before a proposal is
written. {reasoning, success, critique}.
- success=true short-circuits the proposal (bot recovered between
detector tripping and now), saving Pi tokens on false positives.
- critique is spliced into the proposal body via attachCritique() so
the downstream auto-patcher has a sharp spec.
- Graceful: pi missing / timeout / unparseable JSON → proposal still
filed without the critic block.
2. scripts/lint-patch.js (Mindcraft coder._lintCode)
- Pre-flight gate between Pi commit and npm test: node --check, dynamic
import (catches missing named exports), regex extraction of
runSkill("id") calls cross-checked against the live registry.
- Cheaper than npm test, fails fast with a clear reason.
3. runtime/stuck-incident.renderActionTemplate (Voyager action_template.txt)
- All proposal bodies now follow the same fixed-section layout: Task /
Last result / Execution error / State / Metrics / Journal /
Scenarios / Critique / Fix / Edit scope / Forbidden.
4. runtime/skill-library.js (Mindcraft skill_library.getRelevantSkillDocs)
- Word-overlap ranking (Mindcraft's offline fallback) — zero deps,
deterministic. auto-patch.js injects top-3 similar skills into the
Pi prompt as "look at these patterns".
5. runtime/modes.js (Mindcraft modes.js)
- Declarative {name, interrupts, on, active, update(ctx)} chain that
runs BEFORE the curriculum each tick.
- Ships self_preservation (low HP → eat/flee), hunger (food<14 → eat),
night_shelter (night + bed in hand → sleep). Cleaner than ad-hoc
lastFleeAttempt cooldowns in reflex.js.
6. runtime/social/conversation.js + cmd:conv-say/conv-recent/conv-list
- File-JSONL topic channel so two bots from the same repo (different
usernames, different host dirs under state/) can append turns and
read peers. Skeleton — multi-agent collaboration on top later.
Differentiator preserved: every Pi-written skill still lands on main via
auto-patch.js (real git branch + smoke gate + cherry-pick). Voyager
keeps skills in a Chroma JSON, Mindcraft keeps them in RAM — pepa keeps
them as versioned source code reviewable in `git log`.
package.json: 0.0.1 → 0.1.0. 174/174 tests pass. README + AGENTS updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
2.4 KiB
JavaScript
58 lines
2.4 KiB
JavaScript
// Shared IPC contract between runtime/bot.js (server) and tui/tui.tsx (client).
|
|
// Frame format: one JSON object per line over a Unix-domain socket.
|
|
// Socket path: state/<server-key>/bot.sock (created by server, removed on shutdown).
|
|
|
|
export const SOCKET_BASENAME = "bot.sock";
|
|
|
|
// Server → client messages.
|
|
export const EVENT_TYPES = Object.freeze({
|
|
STATUS: "status", // periodic snapshot (HP/food/pos/task/connection)
|
|
LOG: "log", // free-form log line { level, source, text }
|
|
CHAT: "chat", // MC chat { from, text, kind: "player" | "system" }
|
|
DEATH: "death", // death event { reason, position }
|
|
ERROR: "error", // recoverable runtime error { source, text }
|
|
ASK_PI_CHUNK: "ask-pi-chunk", // streamed stdout chunk from Pi subprocess
|
|
ASK_PI_DONE: "ask-pi-done", // Pi subprocess exited { code, durationMs }
|
|
HELLO: "hello", // sent on client connect with current snapshot
|
|
PROPOSAL: "proposal", // pending proposal payload { filename, body }
|
|
});
|
|
|
|
// Client → server commands.
|
|
export const COMMAND_TYPES = Object.freeze({
|
|
PAUSE: "cmd:pause", // reflex loop stops ticking; connection stays
|
|
RESUME: "cmd:resume", // reflex loop resumes
|
|
STOP: "cmd:stop", // graceful disconnect + process exit
|
|
CHAT: "cmd:chat", // { text } sent into MC as bot
|
|
ASK_PI: "cmd:ask-pi", // { prompt } spawn `pi -p` and stream output
|
|
SNAPSHOT: "cmd:snapshot", // request immediate STATUS event
|
|
PROPOSAL_LATEST: "cmd:proposal-latest", // request latest pending proposal
|
|
PROPOSAL_APPROVE: "cmd:proposal-approve", // { filename } move to approved/
|
|
RUN_SKILL: "cmd:run-skill", // { skillId, args? } dispatch a skill once (operator ground-truth probes)
|
|
CONV_SAY: "cmd:conv-say", // { topic, text, intent?, position? } append turn to a multi-agent topic
|
|
CONV_RECENT: "cmd:conv-recent", // { topic, n? } read last n turns
|
|
CONV_LIST: "cmd:conv-list", // list active conversation topics
|
|
});
|
|
|
|
export function encodeFrame(obj) {
|
|
return JSON.stringify(obj) + "\n";
|
|
}
|
|
|
|
// Stateful line splitter — instance per socket.
|
|
export function createLineParser(onObject) {
|
|
let buf = "";
|
|
return (chunk) => {
|
|
buf += chunk.toString("utf8");
|
|
let idx;
|
|
while ((idx = buf.indexOf("\n")) !== -1) {
|
|
const line = buf.slice(0, idx).trim();
|
|
buf = buf.slice(idx + 1);
|
|
if (!line) continue;
|
|
try {
|
|
onObject(JSON.parse(line));
|
|
} catch (e) {
|
|
onObject({ __parseError: true, raw: line, err: String(e) });
|
|
}
|
|
}
|
|
};
|
|
}
|